prowler has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

I have a bunch of modules such as Teacher, Student and Subject that crossreference each other: Teacher has ->get_subjects(), ->get_subject_at($time) etc. but Student has no direct link to Teacher.

I need to subclass one of these classes to provide extra functionality (in a deployment for a university, Lecturer (is a Teacher) has ->get_research projects() as well, but the other classes haven't changed.)

The problem: If the other classes haven't changed then when

$lecturer = $some_subject->get_teacher();
is called (with $some_subject being a Subject) $lecturer is in fact, not a Lecturer.

Obviously I can subclass Student and Subject to Uni_Student and Uni_Subject and overwrite all the cross referencing code to behave as desired:

@subjects = $some_uni_student->get_subjects();
returns an array of Uni_Subjects. However this can involve quite a lot of modification just to enable a change to a single class; the dependencies have to be traced all the way back.

Is there a better way?

Thanks,
Prowler
 - Spelling is a demanding task that requies you full attention.

Replies are listed 'Best First'.
Re: Inheritance and module cross dependencies
by gaal (Parson) on Jan 10, 2005 at 06:59 UTC
    Go over your code replace all your calls to Teacher's constructor with a factory method that returns a teacher that can actually be a Lecturer. (Seminary?) Obviously the factory must have enough information when it is called to know what kind of teaching it is generating.

    Since the rest of the code just assumed Teacher, it should continue to work with a Lecturer (which ISA Teacher). When you do need ->get_research_projects() you can use can to check if it is safe to call it.

    Avoid saying Lecturer explicitly in your code, to what degree this is possible.

      Thanks.

      Now to work out how best to do this without breaking anything...

      Prowler
       - Spelling is a demanding task that requies you full attention.

        Plan:
        1. Create TeacherFactory.pm:

          package TeacherFactory; use Teacher; use strict; sub create_teacher { my ($class, @args) = @_; return Teacher->new(@args); } 1;
        2. Make a list of all files that call Teacher->new.
        3. Insert near their tops a use Teacher; statement.
        4. Search and replace teacher constructions with factory requests.

        That's the easy part; you have a pretty boring factory. Now make it interesting :)

        Most likely, you may find that some calls to Teacher->new give you insufficient information to make the decision about whether a Lecturer or a teacher is required. Refactor your code *after* you have the above in place to either defer or advance the construction, depending on what makes more sense. But move in small steps that don't leave your code unstable.

Re: Inheritance and module cross dependencies
by NetWallah (Canon) on Jan 10, 2005 at 05:19 UTC
    When you need to verify whether the object you got back was a Lecturer, you could do a test something like:
    if (ref($lecturer) eq "LECTURER"){ # You can do $lecutrerer->get_research(); }else{ # This dude can't research ... }
    If there is only a small number of minor differences between the classes, my preferred way would be to add a bit-vector attribute to the "Teacher" class that lists capabilities like "CAN_RESEARCH".

        ..."I don't know what the facts are but somebody's certainly going to sit down with him and find out what he knows that they may not know, and make sure he knows what they know that he may not know, and that's a good thing. I think it's a very constructive exchange," --Donald Rumsfeld

      No. You check if $lecturer is a LECTURER using the isa method, as in
      if( $lecturer->isa('LECTURER') )

      Thanks for the reply.

      It's not that I want to find out which class I've got back (I'd use isa or can for that), but that I want $student->get_random_subject->get_teacher() (etc.) to give me a Lecturer for the deployment that uses Lecturers instead of Teachers, and I want to do this in the cleanest possible way.

      The bit-vector idea doesn't really work in the situation that I'm working with where an arbitrary amount of tweaking can be done between deployments (some of which will be mutually incompatible).

      Prowler
       - Spelling is a demanding task that requies you full attention.