从矩形中推导出正方形违反了利斯科夫的替代原则吗?

我是devise和学习devise原理的新手。

它说从矩形派生矩形是违反李斯科夫替代原则的典型例子。

如果是这样的话,那么正确的devise应该是什么?

我相信推理是这样的:

假设你有一个接受矩形并调整其宽度的方法:

public void SetWidth(Rectangle rect, int width) { rect.Width = width; } 

假定一个矩形是假定这个testing可以通过,那应该是完全合理的:

 Rectangle rect = new Rectangle(50, 20); // width, height SetWidth(rect, 100); Assert.AreEqual(20, rect.Height); 

…因为改变一个矩形的宽度不会影响它的高度。

但是,假设您已经从Rectangle派生了一个新的Square类。 根据定义,一个正方形的高度和宽度总是相等的。 让我们再次尝试一下这个testing:

 Rectangle rect = new Square(20); // both width and height SetWidth(rect, 100); Assert.AreEqual(20, rect.Height); 

该testing将失败,因为设置一个正方形的宽度为100也将改变其高度。

因此,Liskov的替代原则被从矩形中导出的Square违反。

“是”的规则在“现实世界”中是有意义的(正方形绝对是一种矩形),但并不总是在软件devise的世界里。

编辑

要回答你的问题,正确的devise可能应该是矩形和正方形都来自一个普通的“多边形”或“形状”类,它不强制任何有关宽度或高度的规则。

答案取决于可变性。 如果你的矩形和方形类是不可变的,那么Square就是Rectangle一个子types,并且从第二个派生出来是完全可以的。 否则, RectangleSquare都可能暴露一个没有增变的IRectangle ,但是从另一个派生一个是错误的,因为这两个types都不是另一个的子types。

我不同意从矩形派生方必然违反LSP。

在Matt的例子中,如果你的代码依赖于宽度和高度是独立的,那么它确实违反了LSP。

但是,如果您可以在代码中的任何地方用矩形代替矩形,而不会违反任何假设,那么您并不违反LSP。

所以它真的归结为您的解决scheme中抽象矩形的含义。

我最近一直在努力解决这个问题,并且认为我会把自己的帽子放在戒指上:

 public class Rectangle { protected int height; protected int width; public Rectangle (int height, int width) { this.height = height; this.width = width; } public int computeArea () { return this.height * this.width; } public int getHeight () { return this.height; } public int getWidth () { return this.width; } } public class Square extends Rectangle { public Square (int sideLength) { super(sideLength, sideLength); } } public class ResizableRectangle extends Rectangle { public ResizableRectangle (int height, int width) { super(height, width); } public void setHeight (int height) { this.height = height; } public void setWidth (int width) { this.width = width; } } 

注意最后一个类, ResizableRectangle 。 通过将“可resize”移动到子类中,我们在实际改进模型的同时获得了代码重用。 想想这样:一个正方形不能自由resize,而保持正方形,而非正方形的矩形可以。 不是所有的矩形都可以resize,因为一个正方形是一个矩形(在保留其“身份”的同时不能自由resize)。 (o_O)因此,使一个不可resize的基本Rectangle类是有意义的,因为这是一些矩形的额外属性。

让我们假设我们有两个类(为了简单的公共)属性宽度,高度的类Rectangle。 我们可以改变这两个属性:r.width = 1,r.height = 2。
现在我们说Square is_a Rectangle。 但是,尽pipe声明是“一个正方形将performance得像一个矩形”,但我们不能在方形对象上设置.width = 1和.height = 2(如果您设置了高度,您的类可能会调整宽度,反之亦然)。 所以至less有一种情况,Squaretypes的对象的行为不像矩形,因此不能完全替代它们。

我相信OOD / OOP技术的存在使软件能够代performance实世界。 在现实世界中,正方形是具有相等边的矩形。 广场只是一个正方形,因为它具有相同的边,而不是因为它决定了一个正方形。 所以面向对象的程序需要处理。 当然,如果例程实例化对象想要它是方形的,它可以指定长度属性和宽度属性等于相同的数量。 如果使用该对象的程序需要知道,如果它是方形的,只需要问它。 该对象可以有一个名为“Square”的只读布尔属性。 当调用例程调用它时,对象可以返回(Length = Width)。 现在,即使矩形对象是不可变的,情况也是如此。 另外,如果矩形确实是不可变的,Square属性的值可以在构造函数中设置,并且可以用它来完成。 为什么这是一个问题? LSP要求子对象是不可变的,矩形的子对象通常被用来作为其违规的例子。 但是,这似乎并不是很好的devise,因为当使用例程调用对象为“objSquare”时,必须知道它的内部细节。 如果不关心这个矩形是不是方形的,不是更好吗? 那会是因为矩形的方法无论如何都是正确的。 有什么更好的例子说明什么时候LSP被侵犯?

还有一个问题:一个对象是如何变得不可变的? 是否有一个“不可变”的属性,可以在实例化设置?

我find了答案,这是我的预期。 由于我是一个VB.NET开发人员,这正是我所感兴趣的,但是这些概念在各种语言中是相同的。 在VB.NET中,通过使属性为只读来创build不可变类,并使用New构造函数来允许实例化例程在创build对象时指定属性值。 你也可以使用一些属性的常量,它们总是一样的。 从创build向前,对象是不可改变的。

问题在于所描述的实际上不是一个“types”,而是一个累积的紧急财产。

所有你真正拥有的是一个四边形,而“矩形”和“矩形”都是从angular度和边的性质得出的出现的人造物。

“正方形”(甚至矩形)的整个概念只是对象相对于对方和所讨论的对象的属性集合的抽象表示,而不是对象的types和它自身的types。

这就是在无types语言环境下思考问题的原因,因为它不是决定它是“方形”的types,而是决定它是否是“方形”的对象的实际属性。

我想如果你想进一步抽象,你甚至不会说你有一个四边形,但你有一个多边形,甚至只是一个形状。

它很简单:)更多的“基础”类(推导链中的第一个)应该是最一般的。

例如shape – > Rectangle – > Square。

在这里,正方形是一个矩形的特殊情况(具有约束的尺寸),而矩形是一个形状的特例。

换句话说 – 使用“是”一个testing。 乡绅是一个矩形。 但是一个直接的并不总是一个正方形。