zoukankan      html  css  js  c++  java
  • "围观"设计模式(2)--里氏替换原则(LSP,Liskov Substitution Principle)

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

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

    其原文为:

    Let q(x) be a property provable about objectsx of typeT. Thenq(y) should be true for objectsy of typeS whereS is a subtype ofT.

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


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


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


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

    父类Calc:

    package cn.design.pattern2016032004LiskovSubstitutionPrinciple;
    
    public class Calc {
    
    	public void calc(int a, int b) {
    		// a-b = ?
    		System.out.println(a + " - " + b + " = " + (a - b));
    		
    	}
    }
    

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

    package cn.design.pattern2016032004LiskovSubstitutionPrinciple;
    
    public class CalcSon extends Calc{
    
    	public void calc(int a, int b) {
    		// a+b = ?
    		System.out.println(a + " + " + b + " = " + (a + b));
    	}
    	
    	// other method
    	public void addThem(int a, int b) {
    		System.out.println(a + b);
    	}
    }
    

    測试类:这里假设符合里氏替换原则的话,那么应该说将父类的调用的这个地方直接换为子类的话,那么原来的行为不会发生不论什么的改变。

    可是以下的程序证明了,这种做法是违背了里氏替换原则的。将原先父类调用的替换为子类的时候。会由原来的父类的方法:减法。变为如今子类中的:加法。结果发生变化,从而违背了里氏替换原则。

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

    我们在子类继承父类之后,重写了父类的方法时,须要注意,这种做法并不好,减少了整个继承体系的复用性。出错几率会对应的添加。


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

    1. 子类不要覆盖父类的非抽象的方法。

    能够实现其抽象方法。

    2. 子类能够实现自己独有的方法。

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

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



    面对这种情况,一般的,将当前的继承结构解除掉。变为依赖或者聚合组合的形式。

    抽象出更高一层的抽象类,定义好这种一个抽象方法。同一时候由原先的两个类继承实现。


    public abstract class Calculator {
    
    	public abstract void calc(int a, int b);
    }

    public class Calc extends Calculator{
    
    	public void calc(int a, int b) {
    		// a-b = ?
    		System.out.println(a + " - " + b + " = " + (a - b));
    		
    	}
    }

    public class CalcSon extends Calculator{
    
    	public void calc(int a, int b) {
    		// a+b = ?
    		System.out.println(a + " + " + b + " = " + (a + b));
    	}
    	
    	// other method
    	public void addThem(int a, int b) {
    		System.out.println(a + b);
    	}
    }

    通过这种途径将原来的继承结构又一次解构重组后的继承体系,应该说相对来说。出错的几率大大减少了。

    源代码已经上传至GitHub:下载设计模式代码




  • 相关阅读:
    路由器默认密码
    目前网络安全的攻击来源
    SQL注入——时间盲注
    UNIX网络编程第4章4.5listen函数4.6accept函数
    UNIX网络编程第3章套接字编程简介3.2套接字地址结构3.3值结果参数3.4字节排序函数
    Ubuntu软件系列---如何安装deb安装包
    Ubuntu软件系列---添加实时网速
    Ubuntu软件系列---网易云
    4.9 TF读入TFRecord
    4-8 使用tf.train.string_input_producer读取列表样本
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7306128.html
Copyright © 2011-2022 走看看