Perl - 从超类调用子类构造函数(OO)
这可能会成为一个令人尴尬的愚蠢问题,但比可能创建令人尴尬的愚蠢代码要好。 :-) 这确实是一个面向对象设计问题。
假设我有一个对象类“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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
一般来说,
new
方法返回除新对象之外的任何内容是一个错误(设计方面,而不是语法方面)。如果您有时想返回现有对象,请将该方法调用为其他名称,例如new_from_cache()
。我还发现奇怪的是,您不仅将此功能(构造一个新对象并返回一个现有对象)拆分为单独的名称空间,而且还拆分为不同的对象。因此,一般来说,您更接近第二种方法,但您仍然可以让主构造函数 (
new
) 处理各种参数:注意:我不想让事情变得复杂,而您'我仍在学习,但您可能还想看看 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: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.
一般来说,让超类了解其子类是一个坏主意,这一原则延伸到了构造。 [1]如果您需要在运行时决定创建哪种类型的对象(并且您确实这样做了),请创建第四个类来完成这项工作。这是“工厂”的一种。
话虽如此,在回答您的名义问题时,您所描述的问题似乎并不需要子类化。特别是,您显然将根据它们所属的具体类来不同地对待 Foos 的不同类。您真正需要的是一种统一的方法来实例化两个单独的对象类。
那么这个建议如何[3]:将
Foos::Exists
和Foos::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
andFoos::Pending
two separate and unrelated classes and provide (inFoos
) a method that returns the appropriate one. Don't call itnew
; you're not making a newFoos
.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.
如果对象的构造函数返回一个实例,那么它也是一个工厂模式(在 Perl 中不好)放入多个包中。
我会创造这样的东西。如果
names
存在,则is_created
设置为 1,否则设置为 0。我将合并::Pending
和 < code>::Existing 在一起,如果未创建对象,只需将其放入_object
的default
中,检查就会延迟进行。另外,Foo->delete() 和 Foo->change() 将遵循_object
中的实例。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 thanis_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 thedefault
for the_object
, the check happens lazily. Also, Foo->delete() and Foo->change() will defer to the instance in_object
.有趣的答案!当我在代码中尝试不同的东西时,我正在消化它。
好吧,我有同一问题的另一个变体 - 请注意,同一问题只是同一类的不同问题:子类创建问题!
这次:
此代码是一个命令行界面,具有许多不同的复杂选项。我之前告诉过你
/usr/bin/mycrazyfoos
,对吧?好吧,如果我告诉你,二进制文件会根据版本而变化,有时它会完全改变其底层选项,该怎么办?我们正在编写的这个类必须能够解释所有这些事情。目标(或者可能是想法)是:(可能从我们上面讨论的 Foos 类调用):Foos::Commandline,它具有底层 '/usr/bin/mycrazyfoos' 命令的不同版本的子类。
示例:
其中“getlist”和“dance”取决于版本。我想过这样做:
然后
(不同的 .pm 文件,在 Foos/Commandline 的子目录中)
有意义吗?我用代码表达了我想做的事情,但感觉不对,特别是考虑到上述回复中讨论的内容。感觉正确的是,应该有一个命令行的通用接口/超类......并且不同的版本应该能够覆盖它。正确的?希望能就此提出一两个建议。谢谢。
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:
where 'getlist' and 'dance' are version-dependent. I thought about doing this:
then
and (different .pm files, in subdir of Foos/Commandline)
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.