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

前端之家收集整理的这篇文章主要介绍了"围观"设计模式(2)--里氏替换原则(LSP,Liskov Substitution Principle)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

面向对象的程序设计中,里氏替换原则(Liskov Substitution principle)是对子类型的特别定义。它由芭芭拉·利斯科夫(Barbara Liskov)在1987年在一次会议上名为“数据的抽象与层次”的演说中首先提出。

里氏替换原则的内容可以描述为: “派生类(子类)对象能够替换其基类(超类)对象被使用。” 以上内容并非利斯科夫的原文,而是译自罗伯特·马丁(Robert Martin)对原文的解读。其原文为:

Let be a property provable about objects of type . Then should be true for objects of type where is a subtype of .

芭芭拉·利斯科夫与周以真(Jeannette Wing)在1994年发表论文并提出的以上的Liskov代换原则。----维基百科


里氏替换原则我个人的理解是:在继承关系中,父类的对象如果替换为子类的对象,他原来执行的行为依然保持不变,那么这样的程序才符合里氏替换原则,否则违背了里氏替换原则。


下面我们看这样一个实例,体会下,里氏替换原则是在什么情况下违背的。


一个简单的继承结构,在子类中,重写父类方法calc方法

父类Calc:

  1. package cn.design.pattern2016032004LiskovSubstitutionPrinciple;
  2.  
  3. public class Calc {
  4.  
  5. public void calc(int a,int b) {
  6. // a-b = ?
  7. System.out.println(a + " - " + b + " = " + (a - b));
  8. }
  9. }

子类CalcSon,通过将父类中calc这个方法重写为两个数相加。

  1. package cn.design.pattern2016032004LiskovSubstitutionPrinciple;
  2.  
  3. public class CalcSon extends Calc{
  4.  
  5. public void calc(int a,int b) {
  6. // a+b = ?
  7. System.out.println(a + " + " + b + " = " + (a + b));
  8. }
  9. // other method
  10. public void addThem(int a,int b) {
  11. System.out.println(a + b);
  12. }
  13. }

测试类:这里如果符合里氏替换原则的话,那么应该说将父类调用的这个地方直接换为子类的话,那么原来的行为不会发生任何的改变。但是下面的程序证明了,这样的做法是违背了里氏替换原则的。将原先父类调用的替换为子类的时候,会由原来的父类方法:减法,变为现在子类中的:加法。结果发生变化,从而违背了里氏替换原则。

  1. Calc cal = new Calc();
  2. cal.calc(10,20);
  3. /**
  4. * 根据里氏替换原则,当父类替换为子类的时候,使用父类的时候的行为不应该
  5. * 发生变化,那么下面的这段代码,显然发生了变化,这样显然违反了里氏替换
  6. * 原则。
  7. */
  8. CalcSon calcSon = new CalcSon();
  9. calcSon.calc(10,20);

我们在子类继承父类之后,重写了父类方法时,需要注意,这样的做法并不好,降低了整个继承体系的复用性,出错几率会相应的增加


总结前人的诸多经验来看,里氏替换原则主要是有四点:

1. 子类不要覆盖父类的非抽象的方法。可以实现其抽象方法

2. 子类可以实现自己独有的方法

3. 子类的方法重写父类方法的时候,参数部分,要比父类的参数范围要大或者等于(宽松)。释义:举个例子>如果说父类方法中形参是ArrayList,那么,其子类重写这个方法的时候,形参要是List.

4. 子类重写父类方法的时候,返回值要求,父类的返回值要比子类的返回值要小于或者等于。



面对这样的情况,一般的,将当前的继承结构解除掉,变为依赖或者聚合组合的形式。抽象出更高一层的抽象类,定义好这样的一个抽象方法,同时由原先的两个类继承实现。


  1. public abstract class Calculator {
  2.  
  3. public abstract void calc(int a,int b);
  4. }

  1. public class Calc extends Calculator{
  2.  
  3. public void calc(int a,int b) {
  4. // a-b = ?
  5. System.out.println(a + " - " + b + " = " + (a - b));
  6. }
  7. }

  1. public class CalcSon extends Calculator{
  2.  
  3. public void calc(int a,int b) {
  4. System.out.println(a + b);
  5. }
  6. }

通过这样的途径将原来的继承结构重新解构重组后的继承体系,应该说相对来说,出错的几率大大降低了。

猜你在找的设计模式相关文章