在面向對象的程序設計中,裡氏替換原則(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:
package cn.design.pattern2016032004LiskovSubstitutionPrinciple; public class Calc { public void calc(int a, int b) { // a-b = ? System.out.println(a + " - " + b + " = " + (a - b)); } }
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); /** * 根據裡氏替換原則,當父類替換為子類的時候,使用父類的時候的行為不應該 * 發生變化,那麼下面的這段代碼,顯然發生了變化,這樣顯然違反了裡氏替換 * 原則。 */ CalcSon 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); } }