LSP(Liskov Substitution Principle)
定义:子类型必须能够替换掉它们的基类型。
若对每个类型S的对象o1,都存在一个类型T的对象o2。使得在所有针对T编写的程序P中,用o1替换o2后,程序P的功能不变,则说明S是T的子类型!
一个违反该原则的例子
长方形:
public class Rectangle { private double _width; private double _height; public void SetWidth(double w) { _width = w; } public void SetHeight(double w) { _height = w; } public double GetWidth() { return _width; } public double GetHeight() { return _height; } }
正方形:
public class Square extends Rectangle{ public void SetWidth(double w) { super.SetWidth(w); super.SetHeight(w); } public void SetHeight(double w) { super.SetWidth(w); super.SetHeight(w); } }
通常来说继承关系就是is-a的关系,如果一个对象A is-a另一个对象B,那么可以说这个对象A就是从另一个B继承而来。
很显然一个正方形is-a长方形,所以满足这种继承关系。
但是由于正方形4边相等,没有所谓的长宽之分,所以它对setWidth和setHeight做了修改来规避长宽问题。
这个时候当正方形的一个宽发生改变,长也会相应的改变,来实现正方形的4边相等。
public void g(Rectangle r) { r.SetWidth(5); r.SetHeight(4); Assert.AreEqual(r.GetWidth() * r.GetHeight(), 20); }
但是这个时候执行g(Rectangle r) 方法就会出现问题。
上述例子明显的违反了LSP原则,也潜在的违背了OCP原则。
从孤立的角度看,一个模型无法自己进行有意义地验证。模型的正确性仅能通过它的使用者来表达。例如,孤立地看 Square 和 Rectangle,我们发现它们是自洽的并且是有效的。但当我们从一个对基类做出合理假设的程序员的角度来看待它们时,这个模型就被打破了。
结论:
OCP是OOD的核心,而LSP则是OCP的主要原则之一,正是子类的可替换性才使得使用基类类型的模块在无需修改的情况下就可以拓展。
术语is-a的范围过于广泛,有上述例子导致该术语不能有效的定义子类。所以觉得子类的定义应该是“可替换性”。