设计模式 之 里氏代换原则 (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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架