软件设计原则(三)里氏替换原则 -Liskov Substitution Principle

原创 2017年05月10日 17:19:31

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

LSP讲的是基类和子类的关系。只有当这种关系存在时,里氏代换关系才存在。如果两个具体的类A,B之间的关系违反了LSP的设计,(假设是从B到A的继承关系)那么根据具体的情况可以在下面的两种重构方案中选择一种。

-----创建一个新的抽象类C,作为两个具体类的超类,将A,B的共同行为移动到C中来解决问题。

-----从B到A的继承关系改为委派关系。


LSP,Liskov Substitution Principle:

1) If for each object s of type S, there is an objectt of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when s is substituted fort when S is a subtype of T.

2) Functions that use pointers or references to base classes must be able to user objects of derived classes without knowing it.

所有引用基类的地方,都能透明地替换成其子类对象。只要父类能出现的地方,子类就可以出现。


引入里氏替换原则能充分发挥继承的优点、减少继承的弊端。

继承的优点:

  • 代码共享,减少创建类的工作量;每个子类都有父类的方法和属性;
  • 提高代码重用性;
  • 子类可以形似父类,但又异于父类;
  • 提高代码可扩展性;
  • 提高产品开放性。

继承的缺点:

  • 继承是侵入性的——只要继承,就必须拥有父类的属性和方法;
  • 降低代码的灵活性——子类必须拥有父类的属性和方法,让子类自由的世界多了些约束;
  • 增强了耦合性——当父类的属性和方法被修改时,必须要考虑子类的修改。

示例(继承的缺点):

        原有类A,实现减法功能:

  1. class A{    
  2.     public int func1(int a, int b){    
  3.         return a-b;    
  4.     }    
  5. }    
  6.     
  7. public class Client{    
  8.     public static void main(String[] args){    
  9.         A a = new A();    
  10.         System.out.println("100-50="+a.func1(10050));    
  11.         System.out.println("100-80="+a.func1(10080));    
  12.     }    
  13. }   

        新增需求:新增两数相加、然后再与100求和的功能,由类B来负责

  1. class B extends A{    
  2.     public int func1(int a, int b){    
  3.         return a+b;    
  4.     }    
  5.         
  6.     public int func2(int a, int b){    
  7.         return func1(a,b)+100;    
  8.     }    
  9. }    
  10.     
  11. public class Client{    
  12.     public static void main(String[] args){    
  13.         B b = new B();    
  14.         System.out.println("100-50="+b.func1(10050));    
  15.         System.out.println("100-80="+b.func1(10080));    
  16.         System.out.println("100+20+100="+b.func2(10020));    
  17.     }    
  18. }    

OOPS! 原本运行正常的相减功能发生了错误。原因就是类B在给方法起名时无意中重写了父类的方法!


问题由来:

        有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。

解决方案:

        LSP为继承定义了一个规范,包括四层含义:

        1)子类必须完全实现父类的方法

        如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生畸变;则建议不要用继承,而采用依赖、聚集、组合等关系代替继承。

        例如:父类AbstractGun有shoot()方法,其子类ToyGun不能完整实现父类的方法(玩具枪不能射击,ToyGun.shoot()中没有任何处理逻辑),则应该断开继承关系,另外建一个AbstractToy父类。

        2)子类可以有自己得个性

        即,在子类出现的地方,父类未必就能替代。

        3)重载或实现父类方法时,输入参数可以被放大(入参可以更宽松)

        否则,用子类替换父类后,会变成执行子类重载后的方法,而该方法可能“歪曲”父类的意图,可能引起业务逻辑混乱。

        4)重写或实现父类方法时,返回类型可以被缩小(返回类型更严格)

建议:

        在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。

        父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。

        里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。


版权声明:作者原创,转载请附上文章链接。

"围观"设计模式(2)--里氏替换原则(LSP,Liskov Substitution Principle)

在面向对象的程序设计中,里氏替换原则(Liskov Substitution principle)是对子类型的特别定义。它由芭芭拉·利斯科夫(Barbara Liskov)在1987年在一次会议上名为...

【六大设计原则】2. 里氏替换原则 -Liskov Substitution Principle

LSP,Liskov Substitution Principle: 1) If for each object s of type S, there is an objectt of type T ...

设计模式六大原则(2)里氏替换原则(Liskov Substitution Principle)

里氏替换原则(Liskov Substitution Principle)肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。其实原因就是这项原则最早是在1988年,由麻省理工学院的一...

设计原则-里氏替换原则(Liskov Substitution Principle LSP)

规定继承 定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 ...

六大设计原则--里氏替换原则【Liskov Substitution Principle】

定义 最正宗的定义: If for each object o1 of type S there is an object o2 of type T such that for all progr...

OO设计原则 -- Liskov Substitution Principle: OO设计的LSP里氏替换原则

概要  Functions that use pointers or references to base classesmust be able to use objects of derived...
  • Eric_Jo
  • Eric_Jo
  • 2011年09月21日 00:14
  • 5932

设计模式六大设计原则之里氏替换原则(Liskov Substitution Principle)

肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。其实原因就是这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。 定义1:如果对每...

里氏替换原则(Liskov Substitution Principle,LSP)

昨天图文介绍了软件设计的一个基本原则“开闭原则”,而“开闭原则”的核心就是通过抽象把需求变化进行隔离,这种想法可以通过“里氏替换原则”进行保证。理解“里氏替换原则”也是理解面向对象中“运行时多态”的关...

里氏替换原则(Liskov Substitution Principle, LSP)

定义: 第一种:If for each object O1 of type S there is an object O2 fo type T such that for all programs P...

里氏替换原则(Liskov Substitution Principle)

里氏替换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 简单明了的理解:所用引用基类的地方必须透明地使用其子类的对象。...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:软件设计原则(三)里氏替换原则 -Liskov Substitution Principle
举报原因:
原因补充:

(最多只允许输入30个字)