面向對象的原則里氏替換原則

里氏替換原則

里氏替換原則的英文全稱是Liskov Substitution Principle,縮寫是LSP。

LSP第一種定義是:如果對每一個類型的為S的對象O1,都有類型為T的對象O2,使得以T定義的所有程序P在所有對象O1都替換成O2時,程序P的行為不會發生變化,那么類型S是類型T的子類型。這不太好理解。

LSP的第二種定義是:所有引用基類的地方必須能透明地使用其子類的對象。通俗的講就是:只要父類能出現的地方子類就可以出現,而且替換為子類也不會產生任何錯誤或者異常,使用者可能根本就不需要知道是父類還是子類,但是,反過來就不行了,有子類出現的地方,父類未必就能適應。因為子類有的東西父類不一定有。

說了這么多,最終總結就兩個字:抽象。為了便于理解這個原則,我們寫了一個簡單示例來描述,先看UML圖:

里氏替換原則.png

再來看看具體的代碼:

/**
 * 窗口類
 */
public class Window  {

    public show(View chile) {
        chile.draw();
    }
}

/**
 * 建立視圖抽象,測量視圖的寬高為公用代碼,繪制實現交給具體的子類
 */
public abstract class View {
    public abstract void draw();
    public void measure(int width,int height) {
    }
}

/**
 * Button的具體實現
 */
public class Button extends View {

    @Override
    public void draw() {
        //繪制Button
    }
}

/**
 * TextView的具體實現
 */
public class TextView extends View {

    @Override
    public void draw() {
        //繪制TextV
    }
}

上述示例中,Window依賴于View,而View定義了一個視圖抽象,measure是各個子類共享的方法,子類通過覆寫View的draw方法實現各自的功能。任何繼承自View的子類都可以設置給Window的show方法,這就是里氏替換原則。通過里氏替換,就可以自定義各自各樣的view,然后傳遞個Window,Window負責組織view,并且將View顯示到屏幕上。

里氏替換原則的核心是抽象,抽象又依賴于繼承這個特性,在OOP當中,繼承的優缺點都相當的明顯。優點為:

  • 代碼重用,減少創建的成本,每個子類擁有父類的方法和屬性
  • 子類和父類基本相似,但又與父類有所區別
  • 提高代碼的可擴展性,實現父類的方法就可以了,很多開源框架的擴展接口都是通過繼承父類完成的。
  • 提高產品或項目的開放性。

繼承的缺點:

  • 繼承是侵入性的,只要繼承就必須擁有父類的所有屬性和方法
  • 可能造成子類代碼冗余、靈活性降低,因為子類必須擁有父類的屬性和方法
  • 增強了耦合性。當父類的常量、變量和方法被修改時,必須考慮子類的修改,而且在缺乏規范的環境下,這種修好可能帶來非常糟糕的結果---大片的代碼需要重構。

結束

開閉原則中的示例圖片緩存系統也很好地反應了里氏替換原則,即MemoryCache、DiskCache、DoubleCache都可以替換成ImageCache的工作,并保證行為的正確性。實際中,開閉原則和里氏替換原則往往是相互關聯的,通過里氏替換來達到擴展開放、修改關閉的效果。然而,這兩個原則都強調一個OOP的重要特性--抽象。

參考書籍

《Android源碼設計模式》 何紅輝、關愛民著

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容