Perl - 从超类(OO)调用子类构造函数

时间:2010-05-05 19:39:46

标签: perl oop class constructor subclass

这可能会成为一个令人尴尬的愚蠢问题,但可能比创建令人尴尬的愚蠢代码更好。 :-)这是一个OO设计问题,真的。

假设我有一个对象类'Foos'代表一组动态配置元素,这些元素是通过查询磁盘上的命令'mycrazyfoos -getconfig'获得的。假设我想要'Foos'对象有两类行为:

  • 现有的:一个是我刚才提到的命令输出中存在的查询(/ usr / bin / mycrazyfoos -getconfig`。通过炮轰命令对现有命令进行修改。

  • 创建不存在的新的;新的'crazyfoos',使用一组复杂的/usr/bin/mycrazyfoos命令和参数。在这里,我不仅仅是查询,而是实际运行一堆system()命令。影响变化。

这是我的班级结构:

Foos.pm

包Foos,它有一个新的($ hashref-> {name =>'myfooname')构造函数,它接受'crazyfoo NAME',然后查询该NAME的存在以查看它是否已存在(通过炮轰并运行上面的mycrazyfoos命令)。如果该crazyfoo已经存在,则返回一个Foos :: Existing对象。对此对象的任何更改都需要进行外壳修改,运行命令并确认一切正常运行。

如果这是要走的路,那么new()构造函数需要有一个测试来查看要使用的子类构造函数(如果在这种情况下甚至有意义)。以下是子类:

Foos / Existing.pm

如上所述,这适用于Foos对象已存在的情况。

Foos / Pending.pm

如果在上面,'crazyfoo NAME'实际上不存在,那么这个对象将被创建。在这种情况下,上面的new()构造函数将被检查其他参数,并且它将继续使用 - > create()使用system()进行shell输出并创建一个新对象...可能返回一个'现有'一个......

OR

当我输入时,我意识到这可能是最好的单身:

(另类安排)

Foos类,有

- > new()只需一个名字

- > create(),其中包含其他创建参数

- > delete(), - > change()和其他影响存在的参数;必须动态检查。

所以我们这里有两个主要方向。我很好奇哪种方式更聪明。

4 个答案:

答案 0 :(得分:4)

一般来说,new方法返回除了新对象之外的任何内容都是错误的(设计方面,而不是语法方面)。如果您希望有时返回现有对象,请将该方法称为其他方法,例如new_from_cache()

我也觉得奇怪的是,你正在拆分这个功能(构建一个新对象,并返回一个现有的对象),而不仅仅是分开的命名空间,还有不同的对象。所以一般来说,你接近第二种方法,但你仍然可以让主构造函数(new)处理各种参数:

package Foos;
use strict;
use warnings;

sub new
{
    my ($class, %args) = @_;

    if ($args{name})
    {
        # handle the name => value option
    }

    if ($args{some_other_option})
    {
        # ...
    }

    my $this = {
        # fill in any fields you need...
    };

    return bless $this, $class;
}

sub new_from_cache
{
    my ($class, %args) = @_;

    # check if the object already exists...

    # if not, create a new object
    return $class->new(%args);
}

注意:在你还在学习的时候,我不想让事情变得复杂,但你可能也想看看Moose,它会为你处理很多建筑细节,以及属性及其访问者的定义。

答案 1 :(得分:4)

一般来说,超类要了解其子类是一个坏主意,这一原则延伸到构造。[1]如果您需要在运行时决定要创建哪种对象(并且您确实如此),请创建第四个类来完成该任务。这是一种“工厂”。

在回答你的名义问题时,你所描述的问题似乎并没有要求进行子类化。特别是,您显然将根据它们所属的具体类别对待Foos的不同类别。所有你真正要求的是一种统一的方式来实例化两个独立的对象类。

那么这个建议如何[3]:让Foos::ExistsFoos::Pending两个单独的无关的类,并提供(在Foos中)一个返回适当的。不要叫它new;你没有制作新的Foos

如果您希望统一接口,以便客户端不必知道他们正在谈论哪种类型,那么我们可以讨论子类化(或者更好的是,委托给一个懒惰的创建和更新Foos::Handle)。

[1]:解释为什么这是真的是一本书对于一本书来说足够重要[2],但简短的回答是它在子类(依赖于它的超类定义)和超类之间创建了一个依赖循环。 (这是由于一个糟糕的设计决定而依赖于它的子类) [2]:Lakos, John. (1996). Large-scale C++ Software Design. Addison-Wesley.
[3]:不是推荐,因为我不能很好地处理你的要求,以确保我不会在黑暗的海洋中捕鱼。

答案 2 :(得分:2)

如果对象的构造函数将返回一个包含在多个包中的实例,那么它也是factory pattern(Perl中的错误)。

我会创造这样的东西。如果names存在,则is_created设置为1,否则设置为0.我会将::Pending::Existing合并在一起,如果对象是创建只是将其放入default的{​​{1}},检查发生了懒惰。此外,Foo-> delete()和Foo-> change()将推迟到_object中的实例。

_object

答案 3 :(得分:1)

有趣的答案!当我在代码中尝试不同的东西时,我正在消化它。

好吧,我有同一个问题的另一个变体 - 同样的问题,请注意,对同一个类只是一个不同的问题:子类创建问题!

这一次:

此代码是命令行的接口,该命令行具有许多不同的复杂选项。我之前告诉过你/usr/bin/mycrazyfoos,对吗?好吧,如果我告诉你那个二进制文件根据版本发生了变化,有时它会完全改变它的底层选项。而且我们正在编写这门课程,它必须能够解释所有这些事情。目标(或者可能是想法)是:(也许叫做我们上面讨论的Foos类):

Foos :: Commandline,它具有底层'/ usr / bin / mycrazyfoos'命令的不同版本的子类。

示例:

 my $fcommandobj = new Foos::Commandline;
 my @raw_output_list = $fcommandobj->getlist();
 my $result_dance    = $fcommandobj->dance();

其中'getlist'和'dance'是版本相关的。我想过这样做:

 package Foos::Commandline;

 new (
    #Figure out some clever way to decide what version user has
    # (automagically)

    # And call appropriate subclass? Wait, you all are telling me this is bad OO:

    # if v1.0.1 (new Foos::Commandline::v1.0.1.....
    # else if v1.2 (new Foos::Commandline::v1.2....
    #etc

 }

然后

 package Foos::Commandline::v1.0.1;

 sub getlist ( eval... system ("/usr/bin/mycrazyfoos", "-getlistbaby"
  # etc etc

和(不同的.pm文件,在Foos / Commandline的子目录中)

 package Foos::Commandline::v1.2;

 sub getlist ( eval...  system ("/usr/bin/mycrazyfoos", "-getlistohyeahrightheh"
  #etc

有意义吗?我在代码中表达了我想做的事情,但它感觉不对,特别是考虑到上述回答中讨论的内容。感觉正确的是,应该有一个通用的接口/超类到Commandline ......并且不同的版本应该能够覆盖它。对?希望得到一两个建议。格拉西亚斯。