不允许Circle实例接受Point3D作为中心

时间:2017-09-14 10:54:53

标签: oop inheritance dry

我想提交OOP问题。也许它已在其他地方解决了,但我无法找到它......

想象一下这样的(类似python的伪代码)

class Point:
    x: int
    y: int

    # Some methods here    

class Point3D(Point):
    z: int

class Circle:
    center: Point
    radius: int

    # Some methods here

class Sphere(Circle):
    center: Point3D

在静态类型语法中 - 除非我犯了错误 - Point3D个实例将作为center的{​​{1}}属性传递,因为它继承自Circle,必须是不允许的,因为它会变成Point - 就像。

如何在不丢失方法因素化的情况下实现这一目标?

1 个答案:

答案 0 :(得分:1)

Liskov Substitution Principle(LSP)声明用派生类的实例替换base classe的实例应始终有效。这是OOP的核心功能,而不是问题。

问题出在你的模型中 - 让我们考虑2D点和3D点之间的关系。让Point3D继承Point2D表示Point3D Point2D,即所有Point3D&#39}也是Point2D

但这实际上并不符合您的建模!在数学中,只有来自2D平面的3D点(具有z == 0的3D点)实际上是2D点。这种差异是导致您Circle成为球体的漏洞的根源。

对此的OOP模型实际上可能相反:所有2D点都是 3D点,z == 0。因此,从Point2D继承的Point3D会有意义。但是,它会带来一系列可变性的新问题:如果您能够Point2D作为Point3D访问并修改它,那么您可以将其z坐标设置为非零,并最终得到一个不一致的Point2D,它实际上不再是2D。 LSP再次被打破。

在任何方向上表示OOP中2D和3D点之间的关系很诱人:

  • 3D点包含2D点不使用的另一个坐标,因此它应该是派生类

......或......

  • 数学上,2D点也是3D点,因此 it 应该是派生类

...但底线只是两个类都不能继承另一个,因为LSP无论如何都会被破坏。将它们保存为单独的类,带有苹果的苹果和带有Circle的{​​{1}} s,如果需要在点之间考虑公共代码,请使用其他一些语言功能 - 例如,通用编程。