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

I'm not quite sure of the terminology here, but I have a class, and in the new(), or rather _initiate method ( the base class looks after new() ), I want to check for certain conditions, then convert the object to a sub-class, usually I'd do this by...
package DUM::DUMMY; use base 'DUM::Base'; use UNIVERSAL::require; sub _initiate { ... if ($self->{extension}) { my $backend = 'DUM::DUMMY::'.$self->{extension}; $backend->require; bless $self, $backend; }
What's regarded as best-practice in these situations?

Replies are listed 'Best First'.
Re: converting to a sub-class
by Joost (Canon) on Oct 20, 2007 at 17:57 UTC
    Best practices, I don't know, but your construct is limited in that it will only work if the DUM::DUMMY::Whatever objects don't need any initialization (you're just blessing directly into that class).

    What you're doing will work, though it may confuse people. The big question is, do you really need a DUM::DUMMY object at all, when you're going to move it to DUM::DUMMY::Whatever anyway?

    If you can, I would prefer doing something like:

    sub create { my ($class,@options) = @_; my $target_class = $class->find_class_for(@options); return $target_class->new(@options); }
    and have find_class_for return the right package name (and possibly require it). That way the object returned can do whatever it needs to initialize itself - it doesn't even need to be a subclass of DUM::DUMMY.

    Another thing to keep in mind, is that this still requires you to put all the knowledge about which class to choose in the base class. That isn't really "best practice", and it's possible to avoid it, but unless you're got many classes to choose from, it's probably the simplest way to do it like this.

      I see where you're coming from with that, but I'm trying to take a class, then simply to specialise it in whatever way, ie, modified methods, or even new methods (although I doubt I'll be doing that).

      I seem to be having a problem with this approach due to Class::Data::Inheritable and Class::Accessor

      Those are both used in the base class for my 'initial' class to setup the constructor, etc.

      those behaviours from the base class are present in the main class, but not in sub-classes when using the methods I've shown in my first example.
Re: converting to a sub-class
by shmem (Chancellor) on Oct 20, 2007 at 19:48 UTC
    I don't know about best-practice - as long as it works...

    But a note on naming: that rather looks like a "factory class", not like "converting to a subclass". If the class denominated by $self->{extension} has DUM::DUMMY in its @ISA, it is a subclass of DUM::DUMMY. Does the following (add sanity checks for @_ and such) fit into your design?

    package DUM::DUMMY; use base 'DUM::Base'; sub new { my $class = shift; my %args = @_; my $ext; if ($ext = delete $args{extension} ) { my $backend = 'DUM::DUMMY::'.$self->{extension}; eval "require $backend"; if ($@) { # handle errors: die? return undef? set $@ ? } } my $self = $class->SUPER::new(%args); bless $self, 'DUM::DUMMY::'.$ext if $ext; $self; }

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Thanks, much appreciated, but I think that's adding more to the situation, i think a lot of comments have.

      I have My::Base.

      My::Waffle uses My::Base a base class to sort out new() methods, etc, also using Class::Data::Inheritable and Class::Accessor to sort things out for me (this My::Base class wasn't developed by me, but quite the "O'Reilly developer" who was recently working with us.

      I'm quite keen on just having My::Waffle::Whatever simply modify functionality from My::Waffle. The client will never know.

      Does that make any sense? Think about the Person 'class' idea mentioned before with a person class, the client creates a My::Waffle object, which then uses different versions of methods depending on different properties assigned by the client.

      Surely this can't be a new idea/feature wanted?
        mhm. Is that an issue of subclassing, of aggregation, or of mutiple inheritance? Difficult to tell without synopsis.

        See Anno's Alter package (RFC: Alter - Perl extension for Alter Ego Objects); may be that only adds to the confusion, but I have a gut feeling it addresses your problem. Maybe I'm wrong.

        If what you are doing is "method aggregation" (sorry for not using/knowing the canonical term for that), - if you want to call a method based on an object's attribute, you could just use that attribute to handle method dispatch via AUTOLOAD (which more aptly should be called AUTODISPATCH, since loading is only one use for it, as in AutoLoader). The loading of that attribute's package would have to be done in the object constructor.

        Example:

        sub AUTOLOAD { return if $AUTOLOAD eq __PACKAGE__.'::'.'DESTROY'; my $self = shift; (my $func = $AUTOLOAD) =~ s/.*:://; if ($self->{extension}) { my $method = join '::', __PACKAGE__, $self->{extension}, $func +; $method->($self,@_); } else { die "method $func not defined for $self\n"; }; }

        But that has the strong smell of reinventing a wheel. Method lookup based on object attribute? hmm...

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: converting to a sub-class
by gamache (Friar) on Oct 20, 2007 at 17:21 UTC
    That is kind of sick, but I'm sure you have your reasons. For "best practices" I'd suggest using my $backend = __PACKAGE__.'::'.$self->{extension}; instead of hard-coding 'DUM::DUMMY' into the assignment.

    I am fascinated; why do you need to do this?

      In true OO fashion, imagine the 'base' class as a person, i want to create a subclass of a male person.

      so i have

      Person and Person::Male

      I don't want anyone top create a Person::Male object directly, instead just create a Person object, and I'll subclass it to include new methods, or override common methods.

      make sense? Am I missing something completely mental?

      Thanks for your reply, although i was a bit shocked with "it's sick!" :-/
        It's perfectly sane, don't be shocked :-)

        The technique you're using is related to the Factory Pattern. It's quite an established idea.

        Note that the hint to avoid hard-coding (parts of) the class name is good.

      A reply falls below the community's threshold of quality. You may see it by logging in.