SOLID:里氏替换原则 (Liskov Substitution Principle)
里氏替换原则 (Liskov Substitution Principle) 由 Barbara Liskov 于 1987 年提出。她用数学术语描述了该原则,如下所示:
LSP 指导如何在面向对象编程中使用继承。它关于子类型,以及如何从基类型正确派生一个类型。Robert Martin 解释 LSP 如下:
在这里,类型可以是 C# 中的接口、类或抽象类。
让我们进一步简化它。派生类必须能够正确替换其基类。当您从基类派生一个类时,派生类应该正确实现基类的所有方法。它不应该通过抛出 NotImplementedException
来移除某些方法。
考虑以下 IMyCollection
接口,它可以用于创建任何类型的集合类。
public interface IMyCollection
{
void Add(int item);
void Remove(int item);
int Get(int idex);
}
public class MyReadOnlyCollection : IMyCollection
{
private IList<int> _collection;
public MyReadOnlyCollection(IList<int> col)
{
_collection = col;
}
public void Add(int item)
{
throw new NotImplementedException();
}
public int Get(int index)
{
return _collection[index];
}
public void Remove(int item)
{
throw new NotImplementedException();
}
}
上述示例违反了里氏替换原则,因为 MyReadOnlyCollection
类实现了 IMyCollection
接口,但它对 Add()
和 Remove()
方法抛出 NotImplementedException
,因为 MyReadOnlyCollection
类是只读集合,所以不能添加或移除任何项。LSP 建议子类型必须能够替换基类或基接口。在上述示例中,我们应该为只读集合创建另一个不包含 Add()
和 Remove()
方法的接口。
让我们理解“派生类应该正确实现基类方法”的含义。
考虑以下 Rectangle
类
public class Rectangle {
public virtual int Height { get; set; }
public virtual int Width { get; set; }
}
从数学上讲,正方形与四边相等的矩形相同。我们可以在这里使用继承的“is-a”关系。正方形是矩形。Square
类可以继承 Rectangle
类,并使其高度和宽度相等,如下所示。
class Square : Rectangle
{
private int _height;
private int _width;
public override int Height
{
get { return _height; }
set {
_height = value;
_width = value;
}
}
public override int Width
{
get { return _width; }
set
{
_width = value;
_height = value;
}
}
}
以下计算矩形面积
public class AreaCalculator
{
public static int CalculateArea(Rectangle r)
{
return r.Height * r.Width;
}
}
现在,以下返回错误的结果
Rectangle sqr1 = new Square();
sqr1.Height = 6;
sqr1.Width = 8;
Console.WriteLine(AreaCalculator.CalculateArea(sqr1)); //returns 64
Rectangle sqr2 = new Square();
sqr2.Height = 8;
sqr2.Width = 6;
Console.WriteLine(AreaCalculator.CalculateArea(sqr2)); //returns 36
LSP 指出派生类应正确实现基类方法。在这里,正方形类不是矩形类的子类型,因为它具有相等的边。因此,只需要一个属性而不是两个属性(高度和宽度)。这会给类的用户带来困惑,并可能给出错误的结果。