六大原则之 里氏轮换原则

六大原则之 里氏替换原则

                             里氏替换原则

一、什么是里氏替换原则

       先在这里给大家说一下书本对于这一原则的定义:如果对每一个类型为S的对象o1,都有类

 

型为T的对象o2,使得以T定义的所有程序P在所有的对象o1代换o2时,程序P的行为没有变化,那么

 

类型S是类型T的子类型。这是Barbara Liskov教授与eannette Wing教授于1994年提出的一个比较

 

官方的定义,大家也知道这种学科界的大牛思想一般人很难理解。通俗点来讲就是:在继承关系中

 

,父类出现的地方子类也就能出现,并且将父类替换成子类,也不会改变来本的程序。其实他的特

 

点也是对于更变的抗性比较高。在里氏替换原则中有一点很重要的我们要注意:是父类能先出现的

 

地方,子类才能替换,反之子类能先出现的地方父类不一定能出现。

       里氏替换原则的特点:

           就是无论我们是用父类对象还是用子类对象都应该对程序没有任何影响。

二、里氏替换原则的规范

       里氏替换原则是对继承进行契约规范,使其更适用于商业化。

       规范:

    1、子类必须完全实现父类的方法,如果子类要有自己的个性方法也是可以的,但是一定要在

 

父类中声明。为什么要这样呢?其实大家可以这样想,如果一个方法只存在子类中,在父类中不提

 

供相应的声明,则无法在以父类定义的对象中使用该方法。这样就不符合上面说的里氏替换原则的

 

特点。

    2、当子类重载父类的方法是,子类的形参的范围要比父类的形参范围大。(这么设计的好处

 

会在后面进行详细分析)

            

    

三、里氏替换原则更变抗性的体现

       在继承关系中,当子类与父类的方法发生重载是,可能出现的比较严重后果。我在此用两个例子进行对于,就能够很清晰的展露出里氏替换原则的特点:

       例子一:以下是一简单程序的架构代码

   

public 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 Clinet {
	public static void main(String[] args) {
		//有父类的地方就有子类
		Father f= new Father();
		HashMap map = new HashMap();
		f.doSomething(list);
	}
}

 

这个是主程序入口,我们运行结果看一下:

父类被执行...

   当我们根据里氏替换原则用子类的对象替换父类的对象再看一下结果

   

public class Clinet {
	public static void main(String[] args) {
		//有父类的地方就有子类
		Son f =new Son();
		HashMap map = new HashMap();
		f.doSomething(list);
	}
}

 

这个是主程序入口,我们运行结果看一下:

子类被执行...

        从以上的两个例子进行对比,我们不难看出我们用了里氏替换原则后程序发生了变化,这样的变化是不允许的,也是不符合里氏替换原则的。

       因此我们的有一个结论:当子类重载父类的方法时,并且参数范围是子类范围小于父类中的范围,不符合里氏替换原则。

      例子二: 

 

public 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();
		//Son f =new Son();
		HashMap map = new HashMap();
		f.doSomething(map);
	}
}

 

这个是主程序入口,我们运行结果看一下:

父类被执行...

 

  当我们根据里氏替换原则用子类的对象替换父类的对象再看一下结果

     

public class Client {
	public static void main(String[] args) {
		//父类存在的地方,子类就应该能够存在
		Son f =new Son();
		HashMap map = new HashMap();
		f.doSomething(map);
	}
}

这个是主程序入口,我们运行结果看一下:

父类被执行...

       我们可以看出以上的程序利用里氏替换原则对于程序来说没有带来任何变化

      因此我们可以得出结论:子类中重载父类的方法,并且参数范围是子类范围大于父类中的范围时,符合里氏替换原则。

 四、里氏替换原则的总结

        

    优点: 

      1、我们不必关注实例化的是子类还是父类,因为无论实例化的谁对程序结果没有任何影响。  

      2、就是深度规范化继承,让其更适应以后的扩展

   缺点:

      1、因为对于继承做了一些限制,可能会对继承的一些特点进行“封杀”,会一定程度上降低程序的灵活性。