程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 圍觀設計模式(2)

圍觀設計模式(2)

編輯:關於C++

在面向對象的程序設計中,裡氏替換原則(Liskov Substitution principle)是對子類型的特別定義。它由芭芭拉·利斯科夫(Barbara Liskov)在1987年在一次會議上名為“數據的抽象與層次”的演說中首先提出。

裡氏替換原則的內容可以描述為: “派生類(子類)對象能夠替換其基類(超類)對象被使用。” 以上內容並非利斯科夫的原文,而是譯自羅伯特·馬丁(Robert Martin)對原文的解讀。其原文為:

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.

芭芭拉·利斯科夫與周以真(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);
		
/**
* 根據裡氏替換原則,當父類替換為子類的時候,使用父類的時候的行為不應該
* 發生變化,那麼下面的這段代碼,顯然發生了變化,這樣顯然違反了裡氏替換
* 原則。
*/
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);
	}
}

通過這樣的途徑將原來的繼承結構重新解構重組後的繼承體系,應該說相對來說,出錯的幾率大大降低了。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved