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个含义所讲到的,子类的输入參数宽于或等于父类的參数,也就是说这种方法时不会被调用的。


     

  • 相关阅读:
    基本技能训练之线程
    关于UEditor的使用配置(图片上传配置)
    PAT 乙级练习题1002. 写出这个数 (20)
    codeforces 682C Alyona and the Tree DFS
    codeforces 681D Gifts by the List dfs+构造
    codeforces 678E Another Sith Tournament 概率dp
    codeforces 680E Bear and Square Grid 巧妙暴力
    codeforces 678D Iterated Linear Function 矩阵快速幂
    codeforces 679A Bear and Prime 100 交互
    XTUOJ 1248 TC or CF 搜索
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7243314.html
Copyright © 2011-2022 走看看