zoukankan      html  css  js  c++  java
  • 6大设计原则(2):里氏替换原则

    里氏替换原则:LSP

    定义:

    假设对于每个类型为S的对象o1。都有类型为T的对象o2。使得以T定义的全部程序P在全部的对象o1都换为o2时,程序的行为没有发生变化。那么S是T的子类型。

    在继承的时候,父类出现的地方子类就能够出现,子类可替代父类,由于子类中有父类的方法,然而父类却不能够替代子类,由于子类中可能有父类没有的方法。这就是所谓的向下转型是不安全的。

    使用继承有非常多长处,能够提高代码的重用性。提高可扩展性、开放性,可是不可否认,继承也是有缺点的:

    1.继承是侵入性的,仅仅要继承,就必须拥有父类的全部属性和方法;

    2.减少代码的灵活性

    3.增强了耦合性。

    解决方式就是里氏替换原则。

    4个含义:

    1.子类必须全然实现父类的方法

    2.子类能够有自己的方法

    3.覆盖或实现父类的方法时,输入參数能够被放大

    4.覆写或实现父类的方法时。输出结果能够被缩小

    前两个含义比較好理解。这里就不再赘述,主要说一下3和4。

    先说第3个,覆盖或实现父类的方法时。输入參数能够被放大。

    先看一个样例:

    class Father {
    	public Collection dosomething(HashMap map) {
    		System.out.println("父类被运行--->");
    		return map.values();
    	}
    }
    
    class Son extends Father {
    	public Collection dosomething(Map map) {
    		System.out.println("子类被运行--->");
    		return map.values();
    	}
    }
    
    public class Client {
    	public static void main(String[] args) {
    		// 父类存在的地方就能够替换为子类
    		Father f = new Father();
    		HashMap map = new HashMap();
    		f.dosomething(map);
    	}
    }


     

    代码执行结果是:

    父类被运行--->

    依据里氏替换原则,将父类改为子类:

    public class Client {
    	public static void main(String[] args) {
    		// 父类存在的地方就能够替换为子类
    		// Father f = new Father();
    		Son f = new Son();
    		HashMap map = new HashMap();
    		f.dosomething(map);
    	}
    }


     

    然而输出结果还是父类被运行。

    。。

    父类方法的參数是HashMap。而子类方法的參数是Map。也就是说子类的參数类型范围大,子类取代父类传递到调用者中,子类的方法永远不会被运行。

    假设想要运行子类中的方法的话就须要覆写父类中的方法。覆写就是父类中的方法一模一样的出如今子类中。

    class Father {
    	public Collection dosomething(HashMap map) {
    		System.out.println("父类被运行--->");
    		return map.values();
    	}
    }
    
    class Son extends Father {
    	// public void dosomething(Map map) {
    	// System.out.println("子类被运行--->");
    	// }
    	@Override
    	public Collection dosomething(HashMap map) {
    		// TODO Auto-generated method stub
    		System.out.println("子类被运行--->");
    		return map.values();
    	}
    }
    
    public class Client {
    	public static void main(String[] args) {
    		// 父类存在的地方就能够替换为子类
    		// Father f = new Father();
    		Son f = new Son();
    		HashMap map = new HashMap();
    		f.dosomething(map);
    	}
    }


     

    这是正常的。

    假设父类參数的參数类型范围大于子类输入參数类型的话,会出现什么问题呢?会出现父类存在的地方,子类就未必能够存在,由于一旦把子类作为參数传入,调用者就非常可能进入子类的方法范畴。

    改动一下上面的代码。扩大父类參数范围。缩小子类參数范围。

    class Father {
    	public Collection dosomething(Map map) {
    		System.out.println("父类被运行--->");
    		return map.values();
    	}
    }
    
    class Son extends Father {
    	public Collection dosomething(HashMap map) {
    		System.out.println("子类被运行--->");
    		return map.values();
    	}
    
    }
    
    public class Client {
    	public static void main(String[] args) {
    		// 父类存在的地方就能够替换为子类
    		Father f = new Father();
    		Son f1 = new Son();
    		HashMap map = new HashMap();
    		f.dosomething(map);
    		f1.dosomething(map);
    	}
    }

    f运行父类的方法,f1运行子类的方法。

    这就不正常了

    子类在没有覆写父类方法的情况下。子类方法被运行了。所以,子类中方法的參数范围(前置条件)必须与父类的參数范围(前置条件)同样或者更加宽松。

    再来说一下第4个含义,覆写或实现父类的方法时,输出结果能够被缩小。

    什么意思呢?父类方法的返回值是类型T,子类同样方法(重载或覆写)的返回值是S,那么里氏替换原则就要求S必须小于等于T。也就是说。要么S和T类型同样,要么S是T的子类。

    对于覆写而言,父类和子类中的方法时一模一样的,所以返回类型也应当是一样的。

    对于重载,也就是第3个含义所讲到的,子类的输入參数宽于或等于父类的參数,也就是说这种方法时不会被调用的。


     

  • 相关阅读:
    SD卡image 的文件系统分区太小无法安装更多库(如何扩大SD卡rootfs分区)
    ubuntu下生成dtb文件提示:sopc2dts: command not found
    HPS端用于信息打印窗口的 串口的驱动是什么时候加载的呢?
    给 HPS 增添了一个用FPGA逻辑自定义的外设以后, SD卡 image里面哪些文件要更新?
    为何FPGA 外设 IP 与 HPS IP 之间有个 Avalon-MM Pipeline Bridge IP?
    什么是根文件系统(root filesystem)(未完待续)
    为什么preloader和uboot要放置在RAW A2分区?
    重装系统的时候最好选择?(未完待续)
    Android相关知识点面试
    java知识查漏补缺
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7243314.html
Copyright © 2011-2022 走看看