如何处理类中的多态性

Adr*_*ire 6 polymorphism class dynamic-typing

在具有动态类型的语言中,使用多态可能会触发超类上的错误.

我将尝试用一个简单的例子解释我的问题:假设一个动态类型的语言(如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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个非常简单的例子中,当我创建类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? "); 
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

但在这种情况下是调用更新的A :: foo,而不是B :: foo.这意味着其他问题.

3)任何其他解决方案.

作为总结:

如何保护超类代码不受多态影响?

  • 为超级班增加保护.
  • 处理创建子类的这些问题
  • 语言必须这样做!(不知道动态类型语言是否可行)

我正在寻找这个问题的理论/规范解决方案.

编辑:从构造函数中解决问题并澄清一些观点.

jas*_*son 8

通常认为调用实例方法是非常糟糕的做法,特别是virtual在构造函数中的实例方法完全是出于这个原因(但也是因为对象尚未完成"初始化").

3)任何其他解决方案.

医生,我这样做会很痛.

那就不要那样做!

说真的,如果你需要IdA在构造函数中设置A,不要通过调用update来做,通过显式设置IdA构造函数中的值来实现A.


Pat*_*sen 3

基类应该保护自己免受有害的覆盖。为了与开放/封闭原则保持一致,它应该对扩展开放,但对修改封闭。覆盖update是对基类预期行为的有害修改。在您的示例中,重写没有任何好处,update因为A::updateB::update都是处理私有变量的私有方法。根据 中的异常判断,甚至不期望它们应该一起执行B::foo。如果B::update命名不同,您的实现不会有任何问题。无论如何,这可能没问题:因为我所知道的任何语言都不会让你覆盖私有方法,所以B::update可以隐藏A::update而不是覆盖它。

根据语言的不同,您可以以不同的方式限制哪些方法可以被覆盖。有些语言需要一个指示符(通常是关键字或属性)来表明方法可以被重写,而其他语言则需要表明它不能。私有方法通常不可重写,但并非所有语言都具有访问修饰符,并且所有内容实际上都是公共的。在这种情况下,您必须使用 @PoByBolek 建议的某种约定。

tl;dr:孩子与父母的私处没有关系。