cLive ;-) has asked for the wisdom of the Perl Monks concerning the following question:

I have something like this:
use Class; use Class::SubClass; my $obj = Class->new(...authentication vars...);
I want to create a subclass instance. Intuitively, I want to write it like this:
my $subobj = $obj->subclass->new();
while also being able to call it like this:
my $subojb = Class::SubClass->new(...authentication vars...);
Now, I know the former is horrible. It *sort of* works, but it's a cludge. In Class, I need:
sub subclass { my $self = shift; return Class::SubClass->new(%{$self}); }

And then in SubClass, I need to fudge the constructor or at least, that's how I look at it anyway.

Is there a best practice way to do this? I'm probably searching on the wrong term, but I can't seem to get to the meat through the results I find when looking for this.

thoughts?

Replies are listed 'Best First'.
Re: sub classing best practice
by Fletch (Bishop) on Dec 11, 2006 at 14:21 UTC

    Erm . . . blech. Sounds almost like you're grasping at the Abstract Factory pattern, but not quite. You need to give more detail why the superclass needs to know how to create instances of descendants and then someone might catch what you're trying to do.

Re: sub classing best practice
by jbert (Priest) on Dec 11, 2006 at 14:33 UTC
    It sounds like you want to be able to create Class::SubClass both from raw args or as a clone of an existing Class instance.

    Two things come to mind. You could either add a method to Class to extract the original args:

    my $obj = Class->new(...your args...); my $subobj = Class::SubClass->new($obj->original_args);
    Or add a method to SubClass which extracts the needed args from an instance of obj, so you could have:
    my $obj = Class->new(...your args...); my $subobj = Class::SubClass->clone_from_parent($obj); package Class::SubClass; ... sub clone_from_parent { my $class = shift; my $obj = shift; my %args; ...set up %args by calling methods on $obj to get info... return __PACKAGE__->new(\%args); }
    It also might be the case that if we knew more about your situation we could recommend better solutions.
Re: sub classing best practice
by polettix (Vicar) on Dec 11, 2006 at 14:38 UTC
    If all you need is a factory method, why do you have to call the new()? Instead of:
    my $subobj = $obj->subclass->new();
    it's probably sufficient to call:
    my $subobj = $obj->subclass(); # or, better, my $subobj = $obj->subclass_clone();
    Anyway, could you please elaborate a bit regarding the actual usage scenario? It seems that this factory method stiffs the base class, putting a requirement for the base class to "know" about the derived class and making it difficult to use proper inheritance. This would lead to something like:
    my $subobj = $obj->clone_into('Class::SubClass'); # ... sub clone_into { my ($self, $subclass) = @_; return $subclass->new(%$self); }
    which leads us to:
    my $subobj = Class::SubClass->new(%$obj);
    or, probably more cleanly, to:
    my $subobj = Class::SubClass->clone_from($obj); # ... sub clone_from { my ($package, $base) = @_; return $package->new(%$base); }

    Flavio
    perl -ple'$_=reverse' <<<ti.xittelop@oivalf

    Don't fool yourself.

      In answer to some of the questions...

      Both need to use the same authentication mechanism, supplied through 4 arguments. And because of state issues, the authentication state needs to be shared across all transactions. Parent object is authentication + global attribute maniputation, sub classes are manipulation of specific items - in this instance contacts, identities and messages.

      My current solution is indeed:

      my $subobj = $obj->subclass;

      But to me, that doesn't intuitively say I'm creating a new subclass object.

      How many years is it 'til Perl 6 again? ;-)

        Isn't this more of a has-a, rather than is-a? The name of the superclass might give a clue here.

        For example: If it is "Credentials", then I would say that a Message has Credentials, rather than a Message is-a Credential.

        If the different actions are related to each other and can benefit from having a common base class, it might make sense to have an abstract base class which has their shared code/interface etc. But that needn't be the same as your Credentials class.

        But to me, that doesn't intuitively say I'm creating a new subclass object.
        That's why I was suggesting to rename the method to something more readable, like subclass_clone(). Anyway, I agree with Joost about the fact that the authentication stuff should probably reside in an object by its own, whose services are used by your specific manipulation classes.

        Flavio
        perl -ple'$_=reverse' <<<ti.xittelop@oivalf

        Don't fool yourself.
Re: sub classing best practice
by Joost (Canon) on Dec 11, 2006 at 16:01 UTC
    Don't do that. You're hardcoding the subclass's name in the superclass, so now you can only handle 1 subclass. Also, superclasses should NOT know about subclasses. One obvious solution to this problem is to use a seperate factory object to create instances. Doing it this way will also make sure you only pass the relevant attributes to the class constructor.
    package ClassFactory; sub new { my ($class,%auth_vars) = @_; return bless \%auth_vars,$class; } sub create { my ($self,$class) = @_; return $class->new(%$self); }
    Which you can then use as follows:
    my $factory = Class::Factory->new( auth vars ); my $obj = $factory->create("Class"); my $subobj = $factory->create("Class::SubClass");
Re: sub classing best practice
by ysth (Canon) on Dec 11, 2006 at 16:38 UTC
    In Class, I need:
    sub subclass { my $self = shift; return Class::SubClass->new(%{$self}); }
    In package Class, yes, but you can put that snippet, wrapped in a { package Class; ... }, into Class/SubClass.pm.
      Interesting suggestion - but that just feels wrong to me. One class to one file - otherwise debugging becomes bizarre if there's an error. Imagine the confusion parsing X.pm looking for a method that's declared in X::Y.pm uder a package X declaration!
        No more wrong than having a method in X return a new X::Y object. So long as you stick to a consistent way to do your factory classes, I don't see a problem.