在具有动态类型的语言中,使用多态可能会在超类上触发错误。
我将尝试用一个简单的例子来解释我的问题: 假设一种动态类型的语言(如ECMAScript)和以下类结构:
class A{
private idA;
public A(){
idA=0;
}
public foo(){
update();
if (this.idA!=3) Throws new Exception(" What is happening? ");
}
private update(){
this.idA = 3;
}
}
class B extends A{
private idB;
public B(){
super();
idB=0;
}
public foo(){
super.foo();
// Any operation between A::update and B::update()
if (this.idB!=0) Throws new Exception("hmmm, that could not happend!");
update();
}
private update(){
this.idB = 5;
}
}
在这个非常简单的例子中,当我创建一个B类的对象时,B :: foo()调用父A :: foo(),它调用“update”。该对象是B的实例,因此调用的“更新”函数是B :: update,之后,在B :: foo中,再次调用更新函数(B :: update)。最终结果是永远不会调用A :: update,而idA仍为0。
A类在单独使用时工作正常,但在用B扩展后,函数foo()失败。
这个问题的正确解决方法是什么:
1)强制A类调用A :: update,这意味着每次调用自己的函数时都会看到一个丑陋的代码(保护超类):
A::foo(){
A::update();
if (this.idA!=3) Throws new Exception(" What is happening? ");
}
2)B :: update是A :: update的扩展,所以B :: update必须将自己称为父函数(准备子类,并处理问题):
B::foo(){
super.foo();
... // Any operation that must be performed between A::update and B::update
}
B::update(){
super.update();
this.idB = 5;
}
但在这种情况下是调用更新的A :: foo,而不是B :: foo。这意味着其他问题。
3)任何其他解决方案。
总结:
如何保护超类代码不受多态影响?
我正在寻找这个问题的理论/规范解决方案。
编辑:从构造函数中解决问题并澄清一些观点。
答案 0 :(得分:8)
通常认为非常糟糕的做法从构造函数中完全调用实例方法,,尤其是virtual
实例方法出于这个原因(但也是因为尚未完成对象的“初始化”)。
3)任何其他解决方案。
Doc,我这样做会很痛。
然后不要那样做!
说真的,如果您需要在IdA
的构造函数中设置A
,请不要通过调用update
来执行此操作,通过显式设置来执行此操作IdA
的构造函数中A
的值。
答案 1 :(得分:3)
基类应该保护自己免受有害的覆盖。为了与open/close principle保持一致,它应该对扩展开放,但不能修改。覆盖update
是对基类预期行为的有害修改。在您的示例中,覆盖update
没有任何好处,因为A::update
和B::update
都是处理私有变量的私有方法。根据{{1}}中的例外,甚至没有期望它们应该一起执行。如果B::foo
的名称不同,则您的实施不会出现任何问题。无论如何它可能会好的:因为我所知道的语言不会覆盖私有方法,B::update
可以隐藏B::update
而不是覆盖它。
根据语言,您可以限制可以以不同方式覆盖哪些方法。有些语言需要一个指示符(通常是一个关键字或属性),可以覆盖一个方法,其他语言则表明它不能。私有方法通常不会被覆盖,但并非所有语言都具有访问修饰符,并且所有内容都是公开的。在这种情况下,您必须使用@PoByBolek建议的某种约定。
tl;博士:孩子与父母的私生子无关。
答案 2 :(得分:2)
你可能不会喜欢我的答案,但是:惯例和纪律。
建立
的约定记录这些惯例并坚持下去。它们应该是您代码的一部分;无论是注释形式还是命名约定(无论对你有用)。我能想到这样的事情:
/*
* @final
*/
function shouldNotBeOverridden() {
}
/*
* @overridable
* @call-super
*/
function canBeOverriddenButShouldBeCalledFromChildClasses() {
}
/*
* @overridable
*/
function canBeOverridenWithoutBeingCalledFromChildClasses() {
}
这可以帮助读取您的代码的人找出他可能或不可以覆盖的方法。
如果某人仍然覆盖了您的@final
方法,那么您希望能够进行全面测试;)
我喜欢this answer关于python的一些类似问题:
你可以在那里发表评论:
# We'll fire you if you override this method.
答案 3 :(得分:1)
如果语言允许一个类以这种方式调用另一个类的私有方法,那么程序员必须理解并使用它。如果我理解你的目标,应该覆盖foo和更新,并且应该保护更新。然后,他们会在必要时调用父类中的方法。派生的foo方法不需要调用update,因为在父类中调用foo会处理这个问题。代码可以像这样工作:
class A{
private idA;
public A(){
idA=0;
}
public foo(){
update();
if (this.idA!=3) Throws new Exception("idA not set by update");
}
protected update(){
this.idA = 3;
}
}
class B extends A{
private idB;
public B(){
super();
idB=0;
}
@Override
public foo(){
super.foo();
// Any operation between A::update and B::update()
if (this.idB!=5) Throws new Exception("idB not set by super.foo");
}
@Override
protected update(){
super.Update()
this.idB = 5;
}
}
我更改了例外以符合期望。