我正在寻找以下模式。 (我在Perl工作,但我认为语言并不重要。)
有父类Foo和儿童Bar,Baz,Bazza。
构造Foo的方法之一是解析字符串,该字符串的一部分将隐式指定要创建的类。所以例如,如果它开始'http:'那么它就是一个Bar,但是如果它没有,但是它包含'[Date]'然后Baz喜欢它,依此类推。
现在,如果Foo知道它的所有子节点,以及什么字符串是Bar,什么是Baz等,它可以调用相应的构造函数。但基类不应该对其子女有任何了解。
我想要的是Foo的构造函数能够依次尝试它的孩子,直到其中一个人说“是的,这是我的,我会创造的东西”。
我意识到在一般情况下这个问题没有明确定义,因为可能有多个子接受字符串,因此调用它们的顺序很重要:忽略它并假设特征字符串是这样的,只有一个子类会喜欢字符串。
我提出的最好的方法是让子类在初始化时“注册”基类,以便获取构造函数列表,然后循环它们。但是,有一种更好的方法可以让我失踪吗?
示例代码:
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;
}
答案 0 :(得分:5)
也许您可以使用Module::Pluggable实现此功能?这样就无需注册。
我之前采用的方法是使用Module :: Pluggable加载我的子模块(这允许我通过简单地编写和安装它们来添加新的子模块)。每个子类都有一个构造函数,它返回一个受祝福的对象或undef。你遍历你的插件,直到你得到一个对象,然后返回它。
类似的东西:
package MyClass;
use Module::Pluggable;
sub new
{
my ($class, @args) = @_;
for my $plugin ($class->plugins)
{
my $object = $plugin->new(@args);
return $object if $object;
}
}
还有Class:Factory,但根据您的需求,这可能会超出顶部。
答案 1 :(得分:1)
看起来你正试图将一个类同时作为基类和工厂。别。使用2个单独的类。像这样:
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);
}
然后使用它:
my $foo = Factory->get_foo($string);
这样你的基类就不需要了解你的子类,只有你的工厂才知道。并且子类也不需要彼此了解,只有Factory需要知道要尝试的子类的详细信息以及顺序。
答案 2 :(得分:0)
您可以在类Foo中实现任意查找算法,以搜索现有的子类。也许基于子类提供的配置文件,或者您可能想到的任何其他机制。
然后,类Foo将在运行时检测现有的客户端类并依次调用它们。另外,您可以缓存查找结果并接近您已经描述过的注册表解决方案。
答案 3 :(得分:0)
如果您对不包含有关chilidren的信息的父类以及将类子的任务设置为对类本身的适用性的方法进行评论,那么从父类中分解类选择可能是正确的。并为此任务创建一个单例。
至少那是我的偏好...从这个你当前的父类(可能在你的子类中有一些共同的功能)可能会成为抽象或接口。
然后,单身人士可以管理所有子类及其分布的构造(如果它们不起作用则克隆它们吗?)...此外,可以将子类移动到单独的dll中以促进分离。 抱歉,这不是一个直接的解决方案。 我过去通过管理单例类中的类列表就像你在这里一样。单身人士背后的想法是,如果你想要使用任何昂贵的反射,你只需要做一次。