我对SOLID设计原则很陌生。我理解的一个问题是Liskov Substition Principle违规的“方形矩形”示例。为什么Square的高度/宽度设置器会覆盖矩形的高度?当存在多态性时,这究竟是什么导致问题?
不删除此解决问题?
class Rectangle
{
public /*virtual*/ double Height { get; set; }
public /*virtual*/ double Width { get; set; }
public double Area() { return Height * Width; }
}
class Square : Rectangle
{
double _width;
double _height;
public /*override*/ double Height
{
get
{
return _height;
}
set
{
_height = _width = value;
}
}
public /*override*/ double Width
{
get
{
return _width;
}
set
{
_width = _height = value;
}
}
}
class Program
{
static void Main(string[] args)
{
Rectangle r = new Square();
r.Height = 5;
r.Width = 6;
Console.WriteLine(r.Area());
Console.ReadLine();
}
}
按预期输出为30。
答案 0 :(得分:10)
想象一下,用户正在GUI应用程序中实现一个边界框,类似于:
他们希望通过Rectangle
类来表示此蓝框,以便用户点击&拖下它的高度会增加;如果用户向右拖动,其宽度将增加。
LSP声明客户端应该能够使用派生类(Square),无论你在哪里使用它的超类(Rectangle)而不破坏Rectangle的业务逻辑 - 即用户应该能够在一个中为另一个和sub放置一个;其余的代码不应该破坏。
但以下内容彼此不相容:
setWidth
不应影响高度)如果程序员使用Square而不是Rectangle,他们上面的假设将无法工作,就像用户拖下来一样,盒子会在水平方向上变得更大。同时垂直。
Square / Rectangle示例的问题在于我们开始假设过多的Rectangle。矩形可以与其高度具有不同的长度,但这是特定类型的矩形(椭圆形矩形)的属性。
正方形是矩形,但正方形不是长方形矩形。如果我们想假设关于我们Rectangle
类的长椭圆的行为(它的宽度和高度可能不同),那么我们的Square
类从那里扩展是没有意义的。
答案 1 :(得分:1)
LSP声明替换子类的对象不应该改变程序的行为或正确性。您指定的类确实会更改正确性。对于矩形,类的客户端期望高度和宽度可以独立设置。当您使用Square子类时,情况就不再是这样了。
客户端设置宽度为5且高度为10,同时引用恰好为Square但保持在Rectangle变量中的对象,将根据它们设置高度和宽度的顺序得到不同的结果属性。他们可能得到5x5矩形或10x10矩形。两种情况都会出乎意料。
芭芭拉对LSP的原始复杂描述,但是鲍勃叔叔让它更容易 - “使用指针或对基类的引用的函数必须能够使用派生类的对象而不知道它”。这与Square / Rectangle问题有关。
我在http://www.blackwasp.co.uk/SquareRectangle.aspx撰写了一篇关于此事的文章。