Class 如何处理类内的多态性

Class 如何处理类内的多态性,class,polymorphism,dynamic-typing,Class,Polymorphism,Dynamic Typing,在具有动态类型的语言中,多态性的使用可能会触发超类上的错误 我将尝试用一个简单的例子来解释我的问题: 假设一种具有动态类型的语言(如ECMAScript)和以下类结构: class A{ private idA; public A(){ idA=0; } public foo(){ update(); if (this.idA!=3) Throws new Exception(" What is happening?

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

我将尝试用一个简单的例子来解释我的问题: 假设一种具有动态类型的语言(如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)。最终的结果是永远不会调用::update,而idA仍然是0

单独使用时,类A可以正常工作,但使用B扩展后,函数foo()失败

此问题的正确解决方案是什么:

1) 强制类A调用A::update,这意味着每次调用自己的函数都会产生一段难看的代码(保护超级类):

2) update是A::update的扩展,因此B::update必须调用自己的父函数(准备子类并处理问题):

但是在本例中是调用update的A::foo,而不是B::foo。这意味着其他问题

3) 任何其他解决方案

作为总结:

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

  • 在超级类中添加保护
  • 创建子类时处理这些问题
  • 语言必须做到这一点!(不知道是否可以使用动态类型语言)
我正在寻找这个问题的理论/规范解决方案。


编辑:从构造函数中找出问题并澄清一些要点。

通常认为从构造函数中调用实例方法,尤其是
虚拟实例方法是一种非常糟糕的做法,正是因为这个原因(但也因为对象没有完成“初始化”)然而)

3) 任何其他解决方案

医生,我这样做很痛

那就别那么做


说真的,如果您需要在
A
的构造函数中设置
IdA
,请不要通过调用
update
来实现,而要通过在
A
的构造函数中显式设置
IdA
的值来实现,您可能不会喜欢我的答案,但是:约定和纪律

为……订立公约

  • 如果子类可以安全地重写方法而不调用父类实现
  • 当子类必须调用重写方法的父类实现时
  • 当子类不能重写父类方法时
记录这些约定并坚持它们。它们可能是代码的一部分;以注释或命名约定的形式(任何适合您的方式)。我可以想到这样的事情:

/*
 * @final
 */
function shouldNotBeOverridden() {
}

/*
 * @overridable
 * @call-super
 */
function canBeOverriddenButShouldBeCalledFromChildClasses() {
}

/*
 * @overridable
 */
function canBeOverridenWithoutBeingCalledFromChildClasses() {
}
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;
    }
}
这可能有助于阅读您的代码的人了解他可以覆盖或不覆盖哪些方法

如果有人仍然覆盖您的
@final
方法,您希望能够进行彻底的测试;)


我想问一个关于python的类似问题:

您可以在其中添加一条评论,大意如下:

# We'll fire you if you override this method.

如果语言允许一个类以这种方式调用另一个类的私有方法,那么程序员必须理解并接受它。如果我理解您的目标,那么应该覆盖foo和update,并保护update。然后,必要时,他们会在父类中调用该方法。派生的foo方法不需要调用update,因为在父类中调用foo可以解决这个问题。代码的工作原理如下:

/*
 * @final
 */
function shouldNotBeOverridden() {
}

/*
 * @overridable
 * @call-super
 */
function canBeOverriddenButShouldBeCalledFromChildClasses() {
}

/*
 * @overridable
 */
function canBeOverridenWithoutBeingCalledFromChildClasses() {
}
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;
    }
}

我更改了异常以符合预期。

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

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


tl;dr:孩子们与父母的私事无关。

谢谢你的回答,但问题仍然存在:应该是保护自己不受多态性影响的父类、子类还是编程语言?我将对问题进行编辑,以避免这种误解。(不管怎么说,你的答案是+1)当你编辑你的问题时,如果你的问题实质上改变了对它的理解,最好是问一个新问题。我理解你的观点,但问题仍然是一样的。正如我在这个问题的第一个版本中所问,你的解决方案既不是规范的,也不是理论的。你刚刚回答了明显的2%的问题。此外,我使用了一个简单的例子来询问多态性,您回答了一个与多态性无关的构造函数问题。阿德里安·梅尔:嗯,好吧(我不同意你的意见,只是c