1. 程式人生 > 實用技巧 >2里氏替換原則

2里氏替換原則

里氏替換原則

里氏替換原則的英文名稱是:Liskov Substitution Principle,簡稱LSP(老色批)。

1里氏替換原則的定義

英文定義有兩種:①If for each object o1 of type S there is an object o2 of type T such thatfor all programs P defined in terms of S,the behavior of P is unchangedwhen o1 is substituted for o2 then T is a subtype of S.這個定義是最正宗的定義,意思是:如果對一個型別為S的物件o1,都有型別為T的物件o2,使得以S定義的所有程式P在所有的物件o1都代換成o2時,程式P的行為沒有發生變化,那麼型別T是型別S的子型別。②Functions that use pointers or references to base classes must be ableto use objects of derived classes without knowing it.第二個定義意思是:所有引用基類的地方必須能透明地使用其子類物件。清晰明確地說明只要父類能出現的地方子類就可以出現,而且替換為子類也不會產生任何錯誤或異常,使用者可能根本就不需要知道父類還是子類;但是反過來則不可以,有子類的地方,父類未必就能適應。
里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,
所以里氏代換原則是對實現抽象化的具體步驟的規範
繼承存在很多優點,但也存在缺陷,而“里氏替換原則”可以減少"弊"所帶來的麻煩優點:■ 程式碼共享,減少建立類的工作量,每個子類都擁有父類的方法和屬性;■ 提高程式碼的可重用性;■ 提高程式碼的可擴充套件性;■ 提高產品或專案的開放性。
缺陷:■ 繼承是入侵式的。只要繼承,就必須擁有父類的所有屬性和方法;■ 降低程式碼的靈活性。子類必須擁有父類的屬性和方法,使子類受到限制;■ 增強了耦合性。當父類的常量、變數和方法修改時,必須考慮子類的修改,這種修改可能造成大片的程式碼需要重構。

2里氏替換原則的應用

在編譯期間,java語言編輯器會檢查一個程式是否符合里氏替換原則,這是一個無關實現的、純語法意義上的檢查。里氏替換原則要求是使用基類的地方,子類一定適用,因此子類必須具備基類的全部介面。或者說,子型別的介面必須包括全部的基類的介面,而且還有可能更寬。如果一個java程式破壞這一條件,java編輯器就會在編譯程式時丟擲錯誤提示,並停止編譯。例如,一個基類Base聲明瞭一個public方法改換成private或procted。即子類不能
使用一個低訪問許可權的方法覆蓋基類中的高訪問許可權的方法。里氏替換原則為良好的繼承定義了一個規範,它包含4層含義:■ 子類必須完全實現父類的方法;■ 子類可以有自己的個性;■ 覆蓋或實現父類的方法時輸入引數可以被放大;■ 覆蓋或實現父類的方法時輸出結果可以被縮小。例:Animal 是一個表示動物的抽象類,只要是動物就都能動,因此提供一個抽象的move()方法;Horse和Bird都是Animal的子類。
下面編寫一個測試類
public class Test{
    public static void main(String[] args){
        //宣告一個基類物件
        Animal animal;
        //使用基類物件指向子類
        animal = new Horse();
        animal.move;
        animal = new Bird();
        animal.move;
        // Horse h = new Animal(); //錯誤
    }
}
上述程式碼中,使用基類物件指向子類是允許的,但反過來,使用子類物件指向父類則違反里氏替換原則,會出現錯誤。

注意按照里氏替換原則,當多個類之間存在繼承關係時,通常應該使用父類或介面來指向子類的物件(除非需要使用子類特有的方法),這更利於提高系統的可擴充套件性。
在設計模式中體現里氏替換原則的有如下幾個模式:■ 策略模式■ 組合模式■ 代理模式

來自為知筆記(Wiz)