代码之家  ›  专栏  ›  技术社区  ›  Colin Fine

如何创建其派生类由创建属性隐式指定的对象?

  •  3
  • Colin Fine  · 技术社区  · 15 年前

    我正在为以下内容寻找一个模式。(我在Perl中工作,但我不认为语言特别重要)。

    父类foo,子类bar,baz,bazza。

    构造foo的方法之一是解析字符串,该字符串的一部分将隐式指定要创建的类。例如,如果它以“http:”开头,那么它就是一个酒吧;但是如果它没有,但它包含“[日期]”,那么baz喜欢它,以此类推。

    现在,如果foo知道它的所有子级,以及什么字符串是条、什么是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 回复  |  直到 15 年前
        1
  •  5
  •   Nic Gibson    15 年前

    也许你可以用 Module::Pluggable ?这样就不再需要注册了。

    我以前采用的方法是使用module::pluggable来加载我的子模块(这允许我通过简单地编写和安装它们来添加新的子模块)。每个子类都有一个构造函数,该构造函数要么返回一个受祝福的对象,要么返回一个未定义的对象。你循环你的插件直到你得到一个对象,然后返回它。

    类似:

    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 但这可能有点超出你的需要。

        2
  •  1
  •   mpeters    15 年前

    似乎你想让一个类同时成为一个基类和一个工厂。不要。使用两个单独的类。像这样:

    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);
    

    这样,你的基础类就不需要知道你的孩子的课程,只有你的工厂知道。而且,子类也不需要互相了解,只有工厂需要知道要尝试哪个子类的细节,以及按照哪个顺序。

        3
  •  0
  •   mkoeller    15 年前

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

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

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

        4
  •  0
  •   John Nicholas    15 年前

    如果您对不包含chilidren信息的父类以及将建立适合类本身的子类的任务委托给它的方法发表意见,那么从父类中考虑类选择并为此任务创建一个单例可能是正确的。

    至少这是我的偏好…从这个例子中,您当前的父类(可能在子类中具有一些公共功能)可能会变为抽象类或接口。

    然后,singleton可以管理所有子类的构造和它们的分发(如果它们不起作用,克隆它们?)…此外,可以将子类移动到单独的dll中以促进分离。

    抱歉,这不是一个直接的解决方案。 我以前是这样做的,在单例中管理一个类列表,就像您在这里一样。单件背后的想法是,如果你想使用任何昂贵的反射,你只需要做一次。