从矩形推导出方形违反Liskov的替代原则吗?

som*_*raj 58 oop liskov-substitution-principle

我是设计和学习设计原则的新手.

它说从矩形推导出方形是违反Liskov替代原则的典型例子.

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

Ant*_*hyy 76

答案取决于可变性.如果你的矩形和方形类是不可变的,那么Square它实际上是一个子类型,Rectangle从第二个派生出来是完全可以的.否则,Rectangle并且Square可以暴露IRectangle没有变异器,但是从另一个中派生出一个是错误的,因为这两个类型都不是另一个的子类型.

  • 我不这么认为。“IShape”在考虑所有形状(包括非矩形形状)的上下文中是有意义的,但我们在这里讨论的 *this* 上下文则不然。我们这里讨论的只是正方形和长方形;三角形和圆形不需要适用。 (3认同)

Mat*_*ton 60

我相信推理是这样的:

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

public void SetWidth(Rectangle rect, int width)
{
    rect.Width = width;
}
Run Code Online (Sandbox Code Playgroud)

考虑到矩形是什么,假设该测试将通过,这应该是完全合理的:

Rectangle rect = new Rectangle(50, 20); // width, height

SetWidth(rect, 100);

Assert.AreEqual(20, rect.Height);
Run Code Online (Sandbox Code Playgroud)

...因为更改矩形的宽度不会影响其高度.

但是,假设您从Rectangle派生了一个新的Square类.根据定义,正方形的高度和宽度始终相等.让我们再试一次这个测试:

Rectangle rect = new Square(20); // both width and height

SetWidth(rect, 100);

Assert.AreEqual(20, rect.Height);
Run Code Online (Sandbox Code Playgroud)

该测试将失败,因为将正方形的宽度设置为100也将改变其高度.

因此,通过从矩形导出Square来违反Liskov的替换原则.

"is-a"规则在"真实世界"中是有意义的(正方形绝对是一种矩形),但并不总是在软件设计的世界中.

编辑

要回答你的问题,正确的设计应该是Rectangle和Square都来自一个普通的"Polygon"或"Shape"类,它不会强制执行任何有关宽度或高度的规则.

  • 第二个例子,断言应该测试50?顺便说一句,在"现实世界"中,一个正方形要么不可变(正在绘制在页面上),要么在施加力(它由橡胶制成)时它可以不再是正方形,在现实世界中,对象可以动态更改类型我们在编程语言中没有很好地建模. (3认同)

Chr*_*ton 6

我最近一直在努力解决这个问题,并认为我应该加入我的帽子:

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; }

}
Run Code Online (Sandbox Code Playgroud)

请注意最后一堂课ResizableRectangle。通过将“可调整大小”移至子类中,我们可以在实际改进模型的同时重用代码。可以这样想:正方形不能在保持正方形的情况下自由调整大小,而非正方形的矩形可以。但并非所有矩形都可以调整大小,因为正方形就是矩形(并且不能在保留其“身份”的同时自由调整大小)。Rectangle(o_O) 因此,创建一个不可调整大小的基类是有意义的,因为这是某些矩形的额外属性。

  • 请注意,由于字段的可见性,可以通过具有不同的宽度和高度来创建非正方形的“Square”子类。 (3认同)

Han*_*rbe 5

我不同意从矩形中导出square必然会违反LSP.

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

但是,如果你可以在代码中的任何地方用矩形替换一个正方形而不破坏任何假设,那么你就不会违反LSP.

所以,它真的归结为抽象矩形意味着你的解决方案.

  • 这是正确的,但除非您在程序中使用其不变量,否则拥有 Square 类是毫无意义的:) (2认同)