3

我正在寻找以下模式。(我在 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;
}
4

4 回答 4

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,但这可能有点超出您的需求。

于 2009-06-16T12:03:43.833 回答
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 需要知道尝试哪些子类以及以何种顺序尝试的详细信息。

于 2009-06-16T15:56:54.973 回答
0

您可以在类 Foo 中实现任意查找算法,以搜索现有的子类。可能基于子类提供的配置文件,或者您可能想到的任何其他机制。

然后类 Foo 将在运行时检测现有的客户端类并依次调用它们。

另外,您可以缓存查找结果并接近您已经描述过的注册表解决方案。

于 2009-06-16T11:31:11.510 回答
0

如果您对父类的评论不包含有关儿童的信息以及您将建立子类适用性的任务委派给类本身的方法,那么从父类中排除类选择并创建一个可能是正确的这个任务的单身人士。

至少那将是我的偏好......从这个你当前的父类(它可能在你的子类中具有一些通用功能)可能会变成抽象或接口。

然后单例可以管理所有子类的构建及其分布(如果它们不起作用,则克隆它们?)......此外,可以将子类移动到单独的 dll 中以促进分离。

抱歉,这不是一个直接的解决方案。我过去通过管理单例中的类列表来完成此操作,就像您在这里一样。单例背后的想法是,如果您确实想使用任何昂贵的反射,您只需执行一次。

于 2009-06-16T12:26:37.563 回答