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

问题描述 投票:8回答:4

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

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

  • 现有的:一个是我刚刚提到的命令输出中存在的查询(/ usr / bin / mycrazyfoos -getconfig`。通过shelling out命令对现有命令进行修改。
  • 创建不存在的新的;新的'crazyfoos',使用一组复杂的/usr/bin/mycrazyfoos命令和参数。在这里,我不仅仅是查询,而是实际运行一堆system()命令。影响变化。

这是我的班级结构:

foo是.屏幕

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

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

FOOS / Existing.pm

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

FOOS / Pending.pm

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

要么

当我输入时,我意识到这可能是最好有一个:

(另一种安排)

Foos类,有一个

- > new()只需要一个名字

- > create(),它接受额外的创建参数

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

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

perl oop class constructor subclass
4个回答
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,它会为你处理很多关于构造的血腥细节,以及属性的定义和它们的访问者。


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
投票

如果对象的构造函数将返回一个被保存到多个包中的实例,那么它也是一个factory pattern(在Perl中很糟糕)。

我会创造这样的东西。如果names存在而不是is_created被设置为1,否则它被设置为0.我会将::Pending::Existing合并在一起,如果没有创建对象只是将它放入default用于_object,则检查发生懒洋洋。此外,Foo-> delete()和Foo-> change()将遵循_object中的实例。

package Foo;
use Moose;

has 'name' => ( is => 'ro', isa => 'Str', required => 1 );
has 'is_created' => (
    is => 'ro'
    , isa => 'Bool'
    , init_arg => undef
    , default => sub {
        stuff_if_exists ? 1 : 0
    }
);

has '_object' => (
    isa => 'Object'
    , is => 'ro'
    , lazy => 1
    , init_arg => undef
    , default => sub {
        my $self = shift;
        $self->is_created
            ? Foo->new
            : Bar->new
    }
    , handles => [qw/delete change/]
);

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

和(不同的.pom文件,在食物/命令行的子目录中)

 package Foos::Commandline::v1.2;

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

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

© www.soinside.com 2019 - 2024. All rights reserved.