How can I create an object whose derived class is specified implicitly by the creation properties?

constructor, creation, design-patterns, inheritance, perl

Solution

Perhaps you could implement this with Module::Pluggable? This would remove the need for registration.

The approach I've taken before was to use Module::Pluggable to load my child modules (this allowed me to add new child modules by simply writing and installing them). Each of the child classes would have a constructor that either returned a blessed object or undef. You loop over your plugins until you get an object, then return it.

Something like:

package MyClass;
use Module::Pluggable;

sub new
{
    my ($class, @args) = @_;
    for my $plugin ($class->plugins)
    {
       my $object = $plugin->new(@args);
       return $object if $object;
    }
}

There's Class:Factory as well but that may be a little over the top for your needs.

Problem

I'm looking for a pattern for the following. (I'm working in Perl, but I don't think the language matters particularly). With a parent class Foo, and children Bar, Baz, Bazza. One of the methods for constructing a Foo is by parsing a string, and part of that string will implicitly specify which class is to be created. So for example if it starts 'http:' then it's a Bar, but if it doesn't but it contains '[Date]' then Baz likes it, and so on. Now, if Foo knows about all its children, and what string is a Bar, what is a Baz etc, it can call the appropriate constructor. But a base class should not have any knowledge about its children. What I want is for Foo's constructor to be able to try its children in turn, until one of them says "Yes, this is mine, I'll create the thing". I realise that in the general case this problem is not well-defined, as there may be more than one child which will accept the string, and so the order in which they are called matters: ignore this and assume that the characteristics of the string are such that only one child class will like the string. The best I have come up with is for the child classes to 'register' with the base class on initialisation, so that it gets a list of constructors, and then loop through them. But is there a better method that I'm missing? Sample code: ``` package Foo; my @children; sub _registerChild { push @children, shift(); } sub newFromString { my $string = shift; foreach (@children) { my $object = $_->newFromString(@_) and return $object; } return undef; } package Bar; our @ISA = ('Foo'); Foo::_registerChild(__PACKAGE__); sub newFromString { my $string = shift; if ($string =~ /^http:/i) { return bless(...); } return undef; } ```

Original source