2015-07-19 93 views
0

的问题是(构建Java程序,第3版第9章自检问题19):很好的继承设计

试想一个Rectangle类代表二维矩形对象。矩形具有带适当访问器和增变器的宽度和高度字段,以及getArea和getPerimeter方法。
您想在您的系统中添加一个Square类。使Square成为Rectangle的一个子类是否是一个好设计?为什么或者为什么不?

建筑的Java程序给出的答案是:

具有正方形延伸矩形是一个糟糕的设计,因为一个方不能代替一个矩形。如果客户端认为Square是一个矩形并且在其上调用setWidth或setHeight,则会发生意外的结果。客户预计在通话结束后宽度和高度会有所不同,但他们可能不会。

我认为Square应该扩展Rectangle,因为它是一个更具体的Rectangle类型。因此,继承矩形的所有一般性,再加上一个更具体的特征,即宽度和高度是相同的。
我也不明白为什么这本书说这是一个糟糕的设计“因为广场不能代替矩形”。我认为这是相反的,因为如果Rectangle类不能替代Square类,则不能扩展。

+0

“书”?哪本书? –

+0

构建Java程序 - 回到基础的方法,第3版 – CMSC

+0

添加到这个问题,也许呢? –

回答

3

这是一个非常宽泛的问题。最终,我认为这取决于你所做的设计选择。

我能看到的这两面:

  • ,一方面,方是技术上一种长方形的,而且好像这将是什么多态性。另一方面,如果您有一个方形类可以扩展一个矩形类,那么覆盖方法setWidthsetHeight可能会引起混淆。如果您在广场上打电话setWidth,它是否也设置了高度?同样,setHeight更改宽度?因为根据定义,广场必须有相同的边。

这些只是您在编写软件时所做的一些设计选择。我认为这很大程度上取决于你使用这些对象的背景,其他程序员可能会从你的代码中假设什么等等。

0

如果类Square“IS A”Rectangle,继承是可以接受的,但是你必须小心确保这个陈述其实是真的。正如本书所述,其原因是你现在被绑定到超级类中的一个实现,可能不适合子类Square。

如果你想限制你自己,你应该考虑在接口中定义的通用方法,因为这样可以保持你的“IS A”关系为更窄的一组方法,并允许在共享这个接口的类之间有一个更清晰的去耦。

接口还可以让那些你需要某种形式的多重继承的(这是Java不允许,因为它是在C++中它是有问题的,在我看来)的情况下

0
public void foo(final Rectangle r) { 
    r.setWidth(12); 
    System.out.println(r.getWidth() + ", " + r.getHeight()); 
} 

final Square s = new Square(5); 
foo(s); 

那里发生什么?没什么好的。您的Square实现抛出一个运行时异常,或者您的Square不再是方形。两者都不是合理的事情发生。当你扩展一个类时,子类负责维护父类的合同全部继承的方法,被覆盖与否。扩展比大多数人想象的更难以正确执行,您应该尽可能地考虑使用组合和接口继承。

这个概念被称为Liskov替代原则,如果你希望做一些更多的阅读。