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

发布于 2024-08-31 19:44:07 字数 1154 浏览 1 评论 0原文

这可能会成为一个令人尴尬的愚蠢问题,但比可能创建令人尴尬的愚蠢代码要好。 :-) 这确实是一个面向对象设计问题。

假设我有一个对象类“Foos”,它表示一组动态配置元素,这些元素是通过查询磁盘上的命令“mycrazyfoos -getconfig”获得的。假设我希望“Foos”对象具有两类行为:

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

  • 使用一组复杂的 /usr/bin/mycrazyfoos 命令和参数创建新的“crazyfoos”,但实际上运行了一堆 system() 命令,

这是我的类结构:

Foos.pm

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

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

Foos/Existing.pm

如上所述,这是针对 Foos 对象已经存在的情况。

Foos/Pending.pm

如果上面的“crazyfoo NAME”实际上不存在,则会创建一个对象。在这种情况下,上面的 new() 构造函数将检查附加参数,并且它将继续,当使用 ->create() 调用时,使用 system() 退出并创建一个新对象...可能返回一个“现有”一个...

或者

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

另一种安排)

Foos 类,它有一个

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

- >create() 需要额外的创建参数

->delete()、->change() 以及影响现有参数的其他参数;必须动态检查。

因此,我们有两个主要方向。我很好奇哪种方法更明智。

This may turn out to be an embarrassingly stupid question, but better than potentially creating embarrassingly stupid code. :-) This is an OO design question, really.

Let's say I have an object class 'Foos' that represents a set of dynamic configuration elements, which are obtained by querying a command on disk, 'mycrazyfoos -getconfig'. Let's say that there are two categories of behavior that I want 'Foos' objects to have:

  • Existing ones: one is, query ones that exist in the command output I just mentioned (/usr/bin/mycrazyfoos -getconfig`. Make modifications to existing ones via shelling out commands.

  • Create new ones that don't exist; new 'crazyfoos', using a complex set of /usr/bin/mycrazyfoos commands and parameters. Here I'm not really just querying, but actually running a bunch of system() commands. Affecting changes.

Here's my class structure:

Foos.pm

package Foos, which has a new($hashref->{name => 'myfooname',) constructor that takes a 'crazyfoo NAME' and then queries the existence of that NAME to see if it already exists (by shelling out and running the mycrazyfoos command above). If that crazyfoo already exists, return a Foos::Existing object. Any changes to this object requires shelling out, running commands and getting confirmation that everything ran okay.

If this is the way to go, then the new() constructor needs to have a test to see which subclass constructor to use (if that even makes sense in this context). Here are the subclasses:

Foos/Existing.pm

As mentioned above, this is for when a Foos object already exists.

Foos/Pending.pm

This is an object that will be created if, in the above, the 'crazyfoo NAME' doesn't actually exist. In this case, the new() constructor above will be checked for additional parameters, and it will go ahead and, when called using ->create() shell out using system() and create a new object... possibly returning an 'Existing' one...

OR

As I type this out, I am realizing it is perhaps it's better to have a single:

(an alternative arrangement)

Foos class, that has a

->new() that takes just a name

->create() that takes additional creation parameters

->delete(), ->change() and other params that affect ones that exist; that will have to just be checked dynamically.

So here we are, two main directions to go with this. I'm curious which would be the more intelligent way to go.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

∞梦里开花 2024-09-07 19:44:07

一般来说,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,它可以处理很多事情为您介绍构造的具体细节,以及属性及其访问器的定义。

In general it's a mistake (design-wise, not syntax-wise) for the new method to return anything but a new object. If you want to sometimes return an existing object, call that method something else, e.g. new_from_cache().

I also find it odd that you're splitting up this functionality (constructing a new object, and returning an existing one) not just into separate namespaces, but also different objects. So in general, you're closer with your second approach, but you can still have the main constructor (new) handle a variety of arguments:

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

Note: I don't want to complicate things while you're still learning, but you may also want to look at Moose, which takes care of a lot of the gory details of construction for you, and the definition of attributes and their accessors.

一个人练习一个人 2024-09-07 19:44:07

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

话虽如此,在回答您的名义问题时,您所描述的问题似乎并不需要子类化。特别是,您显然将根据它们所属的具体类来不同地对待 Foos 的不同类。您真正需要的是一种统一的方法来实例化两个单独的对象类。

那么这个建议如何[3]:将 Foos::ExistsFoos::Pending 两个独立且不相关的类并提供(在 >Foos) 一种返回适当方法的方法。不要称其为new;您没有制作新的 Foos

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

[1]:解释为什么这是真的对于一本书来说是一个足够重要的主题[2],但简短的答案是它在子类(根据定义依赖于其超类)和超类(它是由于糟糕的设计决策而使其依赖于其子类)。
[2]:拉科斯,约翰。 (1996)。 大规模 C++ 软件设计。艾迪生韦斯利。
[3]:不是建议,因为我无法很好地处理您的要求,以确保我不会在黑暗的海洋中钓鱼。

It is generally speaking a bad idea for a superclass to know about its subclasses, a principle which extends to construction.[1] If you need to decide at runtime what kind of object to create (and you do), create a fourth class to have just that job. This is one kind of "factory".

Having said that in answer to your nominal question, your problem as described does not seem to call for subclassing. In particular, you apparently are going to be treating the different classes of Foos differently depending on which concrete class they belong to. All you're really asking for is a unified way to instantiate two separate classes of objects.

So how's this suggestion[3]: Make Foos::Exists and Foos::Pending two separate and unrelated classes and provide (in Foos) a method that returns the appropriate one. Don't call it new; you're not making a new Foos.

If you want to unify the interfaces so that clients don't have to know which kind they're talking about, then we can talk subclassing (or better yet, delegation to a lazily-created and -updated Foos::Handle).

[1]: Explaining why this is true is a subject hefty enough for a book[2], but the short answer is that it creates a dependency cycle between the subclass (which depends on its superclass by definition) and the superclass (which is being made to depend on its subclass by a poor design decision).
[2]: Lakos, John. (1996). Large-scale C++ Software Design. Addison-Wesley.
[3]: Not a recommendation, since I can't get a good enough handle on your requirements to be sure I'm not shooting fish in a dark ocean.

飞烟轻若梦 2024-09-07 19:44:07

如果对象的构造函数返回一个实例,那么它也是一个工厂模式(在 Perl 中不好)放入多个包中。

我会创造这样的东西。如果 names 存在,则 is_created 设置为 1,否则设置为 0。我将合并 ::Pending 和 < code>::Existing 在一起,如果未创建对象,只需将其放入 _objectdefault 中,检查就会延迟进行。另外,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/]
);

It is also a factory pattern (bad in Perl) if the object's constructor will return an instance blessed into more than one package.

I would create something like this. If the names exists than is_created is set to 1, otherwise it is set to 0.. I would merge the ::Pending, and ::Existing together, and if the object isn't created just put that into the default for the _object, the check happens lazily. Also, Foo->delete() and Foo->change() will defer to the instance in _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/]
);
清风挽心 2024-09-07 19:44:07

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

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

这次:

此代码是一个命令行界面,具有许多不同的复杂选项。我之前告诉过你/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

有意义吗?我用代码表达了我想做的事情,但感觉不对,特别是考虑到上述回复中讨论的内容。感觉正确的是,应该有一个命令行的通用接口/超类......并且不同的版本应该能够覆盖它。正确的?希望能就此提出一两个建议。谢谢。

Interesting answers! I am digesting it as I try out different things in code.

Well, I have another variation of the same question -- the same question, mind you, just a different problem to the same class:subclass creation issue!

This time:

This code is an interface to a command line that has a number of different complex options. I told you about /usr/bin/mycrazyfoos before, right? Well, what if I told you that that binary changes based on versions, and sometimes it completely changes its underlying options. And that this class we're writing, it has to be able to account for all of these things. The goal (or perhaps idea) is to do: (perhaps called FROM the Foos class we were discussing above):

Foos::Commandline, which has as subclasses different versions of the underlying '/usr/bin/mycrazyfoos' command.

Example:

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

where 'getlist' and 'dance' are version-dependent. I thought about doing this:

 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

 }

then

 package Foos::Commandline::v1.0.1;

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

and (different .pm files, in subdir of Foos/Commandline)

 package Foos::Commandline::v1.2;

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

Make sense? I expressed in code what I'd like to do, but it just doesn't feel right, particularly in light of what was discussed in the above responses. What DOES feel right is that there should be a generic interface / superclass to Commandline... and that different versions should be able to override it. Right? Would appreciate a suggestion or two on that. Gracias.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文