Question

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;
}
Was it helpful?

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.

OTHER TIPS

It seems you're trying to have a single class be both a base class and a factory. Don't. Use 2 separate classes. Something like this:

package Foo;

package Bar;
use base 'Foo';

package Baz;
use base 'Foo';

package Bazza;
use base 'Foo';

package Factory;
use Bar;
use Baz;
use Bazza;

sub get_foo {
    my ($class, $string) = @_;
    return Bar->try($string) || Baz->try($string) || Bazza->try($string);
}

And then use it like:

my $foo = Factory->get_foo($string);

This way your base class doesn't need to know about your child classes, only your factory does. And child classes don't need to know about each other either, only Factory needs to know the details of which child classes to try and in which order.

You may implement a arbitrary lookup algorithm in class Foo, that searches for existing child classes. Maybe based on config files provided with child classes, or with any other mechanism you might think of.

The class Foo will then detect the existing client classes at runtime and call them in turn.

Additionaly you may cache the lookup results and come close to the registry solution you already described yourself.

If you take your comment about the parent class not containing info about the chilidren and your method of delegating the task of establishing a child classes suitability to the class itself then it is probably correct to factor out the class selection from the parent class and create a singleton for this task.

at least that would be my preference ... from this your current parent class (which presumably has some common functionality across your child classes) can presumably then either become abstract or an interface.

the singleton could then manage the construction of all the child classes and their distribution (clone them if they are not functional?) ... moreover the child classes can be moved into a separate dll to promote separation.

sorry that is not a direct solution. I have done this in the past by managing a list of classes in the singleton much like you are here. the idea behind the singleton is that if you do want to use any expensive reflection you only have to do it once.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top