【发布时间】:2020-03-04 10:26:36
【问题描述】:
我知道当我想将此机制应用于派生类的 Perl 对象时,Perl 不是静态类型的:
假设我有一个基类B 和一个继承自B 的派生类D。
我还有一个对象$obj,它包含一个D 对象。
函数 Bf() 需要 B 类型的参数。
显然(根据多态性规则)我可以像Bf($obj) 一样将$obj 传递给Bf(),但与静态类型语言不同的是Bf() 将看到整个D 对象(而不仅仅是B 的元素)。
在 Perl 中是否有一个(相当干净和简单的)解决方案来解决这个问题?解决方案应该“隐藏”B 在Bf() 中的D 中没有的属性(和方法),而不是限制对原始B(实际上是D)的修改。
仅限成人程序员(2020 年 3 月 6 日添加)
好的,人们想要更具体的描述。 不幸的是(正如所指出的)原始程序非常复杂,并且使用类似反射的机制来自动生成 getter、setter 和 formatter,我真的不能在这里给出一个最小的工作示例,因为它会不是最小的。
首先我有一个类MessageHandler 处理消息(不足为奇!)。
然后我有一个函数log_message($$$),它期望(以及其他)MessageHandler 对象作为第一个参数。
然后我就有了这个类的层次结构(实际上它要复杂得多):
MessageHandler
ControlMessageHandler (ISA: MessageHandler)
ControlMessageResponseHandler (ISA: ControlMessageHandler)
现在如果log_message 想要MessageHandler,我可以传递ControlMessageResponseHandler,因为它符合MessageHandler。
但是这样做会将ControlMessageResponseHandler 的所有属性暴露给log_message,而这些属性在MessageHandler 中是不存在的。
危险在于log_message 可能(错误地)访问ControlMessageResponseHandler 的属性,该属性在MessageHandler 中不存在。为了防止错误,我想防止这种情况发生,或者至少得到一些警告(就像我会使用静态类型的语言,如 Eiffel)。
里面的脏细节
以防万一,我将概述我的数组对象是如何构建的(一个工作示例需要大量额外的代码):
首先数组索引是这样自动分配的:
use constant I_VERBOSITY => IS_NEXT->(); # verbosity level
use constant I_TAG => IS_NEXT->(); # additional tag
use constant I_TAG_STACK => IS_NEXT->(); # tag stack
use constant I_MSG_DEBUG => IS_NEXT->(); # handler for debug messages
...
use constant I_LAST => IS_LAST->(); # last index (must be last)
I_LAST 是继承所必需的。
属性定义如下:
use constant ATTRIBUTES => (
['verbosity', I_VERBOSITY, undef],
['tag', I_TAG, \&Class::_format_string],
['tag_stack', I_TAG_STACK, undef],
['msg_debug', I_MSG_DEBUG, \&Class::_format_code],
...
);
定义包含如何格式化每个属性的提示。 此信息用于设置格式化程序以格式化每个属性,如下所示:
use constant FORMATTERS =>
(map { Class::_attribute_string($_->[0], $_->[1], undef, $_->[2]) }
ATTRIBUTES); # attribute formatters
Getter 和 setter 自动定义如下:
BEGIN {
foreach (ATTRIBUTES) {
Class::_assign_gs_ai(__PACKAGE__, $_->[0], $_->[1]);
}
}
构造函数将使用以下行:
my $self = [];
$#$self = I_LAST;
$self->[I_VERBOSITY] = $verbosity;
...
最后我的对象打印例程是这样的:
sub as_string($)
{
my $self = shift;
my $a_sep = ', ';
return join($a_sep, map { $_->($self, $a_sep) } FORMATTERS);
}
通过继承看起来像这样:
sub as_string($)
{
my $self = shift;
my $a_sep = ', ';
return join($a_sep, $self->SUPER::as_string(),
map { $_->($self, $a_sep) } FORMATTERS);
}
【问题讨论】:
-
请说明问题所在。以与使用父类相同的方式使用子类是很常见的,而且通常不是问题。
-
您想要实际完成的最小工作示例会有所帮助。你的构造函数是什么样的?为什么
Bf()需要“只是B的元素”,“元素”到底是什么意思? -
但是,如果它对于
Bf()是否获得B或D的对象很重要(因为它使用的方法在这些类之间有所不同)- 似乎是这样,否则就没有问题了......那么向它发送D对象是一个明显的错误,不是吗? -
显示代码后的几个cmets。我认为这是关于
as_stringsub。 (1) 似乎一个对象总是第一个参数——然后让它成为一个方法?那么毫无疑问,每个班级都有自己的班级。 (2) 如果这实际上 is a method 那么原型具有误导性(它被忽略为一个方法),我看不出这个问题的理由,因为你可以制作派生类的版本来做什么你想要 (3) 有了这么多的复杂性,我肯定会简化,所以取所需的对象或称其为错误。 /// 我想说你在这个页面上有很多东西可以做你的决定 -
我引用的编辑要求一个对象丢失添加的位,但要保留它从父级更改的位,我提到了这一点;授予,我不知道那里的所有工具,但是通常理解的“动态绑定”给出了这个或那个,不是这个的一部分,但也不完全是那个。在 Perl 中,我们根本没有“动态绑定”。如果你真的想要它,你必须实现它,如果你想知道添加了哪些方法,你 需要
can或类似的方法。至于原型,它们只是被方法忽略了,这就是我的意思。
标签: perl oop inheritance