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

I'm sure I've asked this in the cb or on #perl before, but I don't remember the suggestions given to me, so I'll ask it here for posterity.

I have a suite of classes (Thingy, Thingy::Cog, Thingy::Handle, Thingy::Ramp, etc.) that can be inherited from so that their methods can be overridden. Thing is (no pun intended), Thingy objects need to generate Thingy::Cog objects. Therefore, when a class derives from Thingy (such as MyThingy) it should auto-generate inheriting classes with the same prefix (autovivifying MyThingy::Cog, MyThingy::Handle, etc.).

I'd rather not hard-code more than I have to in this case, unless a purely dynamic solution is disgusting to look at and code.

Update: I apologize if I was unclear. Here's a better explanation. A Thingy object produces a Thingy::Cog object. If I sub-class Thingy as MyThingy, I want the MyThingy object to attempt to create a MyThingy::Cog object instead of the hardcoded Thingy::Cog object. If MyThingy::Cog doesn't exist, then Thingy::Cog should be the class used.

What this means is that I have this code...

package Thingy; sub get_cog { my ($self) = @_; return Thingy::Cog->new; }
and I want to make it extensible when Thingy is subclassed (and Thingy::Cog is potentially subclassed). I could do
sub get_cog { my (@self) = @_; return( (ref($self) . "::Cog")->new ); }
but that breaks if the person subclassing Thingy has not also subclassed Thingy::Cog.

Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

Replies are listed 'Best First'.
Re: Inheritence in a Suite of Modules
by jdporter (Paladin) on Mar 27, 2006 at 22:37 UTC

    My solution for this is usually to define methods in the root class that return the names of the aux classes.
    In the base class (Thingy), these would return 'Thingy::Cog', 'Thingy::Handle', etc.
    In the derived class (MyThingy), these would return 'MyThingy::Cog', 'MyThingy::Handle', etc.
    In that way, the differences between the base and the derived classes are kept minimal and isolated.
    When the Thingy method that creates a cog object gets called, it refers to the above method to get the name of the class to instantiate.

    Of course, this technique requires that the interfaces to the constructors in the base and derived aux classes are kept strictly compatible.

    package Thingy; sub cog_class { 'Thingy::Cog' } sub get_cog { my $self = shift; $self->cog_class->new(@_) } package MyThingy; use base Thingy; sub cog_class { 'MyThingy::Cog' }

    (It should not be necessary to re-define these name-returning methods in any of the other classes, even if you have, for example, a Cog creating a Ramp, because the cog object should have a reference to the relevant Thingy object, so it can get the name from that, if necessary.)

    package MyThingy::Cog; sub get_ramp { my $self = shift; $self->thingy->ramp_class->new(@_) }
    We're building the house of the future together.
Re: Inheritence in a Suite of Modules
by ikegami (Patriarch) on Mar 27, 2006 at 21:30 UTC

    The following snippets allow you to create objects of the right class. You did not ask for this, but I'm missing the point of creating classes automatically. What is the good of having two identical classes? Answer me this, and I'll answer your original question. (In fact, I've already written the answer.)

    package Thingy; sub make_cog { my $self = shift; return Cog->new(@_); } { ... my $cog = $self->make_cog(...); ... } package MyThingy; sub make_cog { my $self = shift; return MyCog->new(@_); }

    or (less recommended)

    package Thingy; sub make_cog { my $self = shift; my $cog_class = ref($self) . '::Cog'; if ($cog_class->can('new')) { return $cog_class->new(@_); } else { return Thingy::Cog->new(@_); } }

    Update: Reguarding your update which greatly clarified things, the second snippet should do the trick.

Re: Inheritence in a Suite of Modules
by xdg (Monsignor) on Mar 27, 2006 at 22:16 UTC

    It's really unclear what you want to accomplish. It sounds like you want to subclass the root of an inheritance three and auto-generate the rest of the inheritance accordingly -- but then you're in a multiple inheritance situation, right? Do you mean @MyThingy::Handle::ISA = qw( MyThingy Thingy::Handle )?

    I suppose this could be done just with @ISA munging, though it's ugly:

    [Original code deleted; see update below]

    Update for japhy's update: Ah! Makes much more sense. How about something along these lines:

    sub get_cog { my ($self) = @_; my $class = ref $self; if ( $class eq "Thingy" ) { return Thingy::Cog->new; } else { my $cog_class = "$class\::Cog"; (my $cog_class_file = $cog_class . "pm") =~ s{::}{/}; if ( grep { -e $_ } map { $_/$cog_class_file } @INC ) { require $cog_class_file; return $cog_class->new; } else { return Thingy::Cog->new; } } }

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Inheritence in a Suite of Modules
by roboticus (Chancellor) on Mar 27, 2006 at 21:09 UTC
    japhy--

    Are you sure you need those other classes generated? It sounds wrong to me. Since you're going to auto-generate them, it seems that the X::Cog, etc., classes would all have the same interface to the X:: class as Thingy::Cog does to Thingy. So it seems to me that you can subclass Thingy without having to change the behaviour of X::Cog, et. al.

    Can you give a bit more detail so I can see what you're after?

    --roboticus