設計模式的七大原則(4) --里氏替換原則

正號先生發表於2019-08-05

前言

上一節中我們介紹了,依賴倒置,依賴倒置利用抽象的穩定性來架構我們的系統,是我們經常能遇到的一種原則,比如說面向介面程式設計。

這一節中,我們來說說里氏替換原則,這個原則其實非常非常的簡單,其實與依賴倒置相結合來看,就是希望我們用抽象的方法來構建專案而非具體的實現,里氏替換原則就是推薦我們不要重寫父類中具體的實現來構建我們的專案。

我們來深入研究研究。

基本介紹

  1. 繼承包含這樣一層含義:父類中凡是已經實現好的方法,實際上是在設定規範和契約,雖然它不強制要求所有的子類必須遵循這些契約,但是如果子類對這些已經實現的方法任意修改,就會對整個繼承體系造成破壞。
  2. 繼承在給程式設計帶來便利的同時,也帶來了弊端。比如使用繼承會給程式帶來侵入性,程式的可移植性降低,增加物件間的耦合性,如果一個類被其他的類所繼承,則當這個類需要修改時,必須考慮到所有的子類,並且父類修改後,所有涉及到子類的功能都有可能產生故障

那麼翻譯成比較容易理解的話,就是說,子類一般不該重寫父類的方法,因為父類的方法一般都是對外公佈的介面,是具有不可變性的,你不該將一些不該變化的東西給修改掉。

上述只是通常意義上的說法,很多情況下,我們其實不必要太過於在意這個原則,因為相對於繼承帶給我們的好處,很多時候我們會忽略掉其帶來的弊端。比如預設介面卡,裝飾器模式等一些設計模式

不過就算如此,如果你真的遇見了不得不重寫父類方法的場景,那麼請你考慮,你是否真的要把這個類作為子類出現在這裡,或者說這樣做所換來的是否能彌補你失去的東西,比如子類無法代替父類工作,那麼就意味著如果你的父類可以在某一個場景裡工作的很正常,那麼你的子類當然也應該可以,否則就會出現下述場景。

案例

//某一個類
public class SomeoneClass {
    //有某一個方法,使用了一個父類型別
    public void someoneMethod(Parent parent){
        parent.method();
    }
}

父類程式碼如下

public class Parent {

    public void method(){
        System.out.println("parent method");
    }
}

結果我有一個子類把父類的方法給覆蓋了,並且丟擲了一個異常。

public class SubClass extends Parent{

    //結果某一個子類重寫了父類的方法,說不支援該操作了
    public void method() {
        throw new UnsupportedOperationException();
    }
    
}

這個異常是執行時才會產生的,也就是說,我的SomeoneClass並不知道會出現這種情況,結果就是我呼叫下面這段程式碼的時候,本來我們的思維是Parent都可以傳給someoneMethod完成我的功能,我的SubClass繼承了Parent,當然也可以了,但是最終這個呼叫會丟擲異常。

public class Client {

    public static void main(String[] args) {
        SomeoneClass someoneClass = new SomeoneClass();
        someoneClass.someoneMethod(new Parent());
        someoneClass.someoneMethod(new SubClass());
    }
}

這就相當於埋下了一個個陷阱,因為本來我們的原則是,父類可以完成的地方,我用子類替代是絕對沒有問題的,但是這下反了,我每次使用一個子類替換一個父類的時候,我還要擔心這個子類有沒有給我埋下一個上面這種炸彈。

所以里氏替換原則是一個需要我們深刻理解的原則,因為往往有時候違反它我們可以得到很多,失去一小部分,但是有時候卻會相反,所以要想做到活學活用,就要深刻理解這個原則的意義所在。

總結

到這裡我們已經掌握了四個原則(單一職責原則,介面隔離原則,依賴倒置原則,里氏替換原則)

下一個原則是非常非常重要的原則。甚至說,我們前面講的都是為了下一個開閉原則做的準備也不為過。所以說掌握這四種原則是非常非常重要的。

相關文章