设计模式 之 里氏代换原则 (Liskov's Substitution Principle)
Motivation
动机
All the time we design a program module and we create some class hierarchies. Then we extend some classes creating some derived classes.
一直以来,我们设计程序模块,并且建立类的层次关系,然后扩展类来创建派生类。
We must make sure that the new derived classes just extend without replacing the functionality of old classes. Otherwise the new classes can produce undesired effects when they are used in existing program modules.
我们必须保证新派生的类只是扩展,而没有替换旧类中的功能。否则当新类用在已有程序模块中时将可能产生不期望的影响。
Likov's Substitution Principle states that if a program module is using a Base class, then the reference to the Base class can be replaced with a Derived class without affecting the functionality of the program module.
里氏代换原则要求如果一个程序模块使用了基类,那么对基类的引用能够使用派生类替代,并且不会影响程序模块的功能。
Intent
目的
Derived types must be completely substitutable for their base types.
派生类型必须可以完全替代它们的基类型。
Example
示例
Below is the classic example for which the Likov's Substitution Principle is violated. In the example 2 classes are used: Rectangle and Square. Let's assume that the Rectangle object is used somewhere in the application. We extend the application and add the Square class. The square class is returned by a factory pattern, based on some conditions and we don't know the exact what type of object will be returned. But we know it's a Rectangle. We get the rectangle object, set the width to 5 and height to 10 and get the area. For a rectangle with width 5 and height 10 the area should be 50. Instead the result will be 100
下面是一个违反里氏代换原则的很典型的例子。例子中使用了2个类:Rectangle和Square。我们假设Rectangle对象在引用中的某个地方使用。我们扩展引用,添加Square类。square类使用工厂模式返回,在某些条件下,我们不能明确的指定哪种类型会返回。但是我们知道,他是Rectangle。我们得到了rectangle对象,设置宽度为5,高度为10,得到了一个区域。对于矩形来说宽度5高度10面积应该是50,而对于正方形来说结果是100。
1 // Violation of Likov's Substitution Principle 2 class Rectangle 3 { 4 protected int m_width; 5 protected int m_height; 6 7 public void setWidth(int width){ 8 m_width = width; 9 } 10 11 public void setHeight(int height){ 12 m_height = height; 13 } 14 15 16 public int getWidth(){ 17 return m_width; 18 } 19 20 public int getHeight(){ 21 return m_height; 22 } 23 24 public int getArea(){ 25 return m_width * m_height; 26 } 27 } 28 29 class Square extends Rectangle 30 { 31 public void setWidth(int width){ 32 m_width = width; 33 m_height = width; 34 } 35 36 public void setHeight(int height){ 37 m_width = height; 38 m_height = height; 39 } 40 41 } 42 43 class LspTest 44 { 45 private static Rectangle getNewRectangle() 46 { 47 // it can be an object returned by some factory ... 48 return new Square(); 49 } 50 51 public static void main (String args[]) 52 { 53 Rectangle r = LspTest.getNewRectangle(); 54 55 r.setWidth(5); 56 r.setHeight(10); 57 // user knows that r it's a rectangle. 58 // It assumes that he's able to set the width and height as for the base class 59 60 System.out.println(r.getArea()); 61 // now he's surprised to see that the area is 100 instead of 50. 62 } 63 }
Conclusion
结论
This principle is just an extension of the Open Close Principle and it means that we must make sure that new derived classes are extending the base classes without changing their behavior.
本原则只是开放封闭原则的一个扩展,它意味着我们应该确保新派生的类应该在不改变其基类行为的基础之上进行扩展。
原文地址:https://www.oodesign.com/liskov-s-substitution-principle.html