設計模式-6大設計原則總結

名稱 英文名稱 解釋
單一職責原則 Single Responsibility Principle 一個類只負責一項職責
里氏替換原則 Liskov Substitution Principle 父類能出現的地方都可以替換為子類,反之則不一定
依賴倒置原則 Dependence Inversion Principle 抽象不應該依賴于細節,細節應該依賴于抽象
接口隔離原則 Interface Segregation Principle 客戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上。
迪米特法則 Law of Demeter 一個對象應該對其他對象保持最少的了解。降低類與類之間的耦合(高內聚,低耦合)
開閉原則 Open Closed Principle 軟件實體應當對擴展開放,對修改關閉,即軟件實體應當在不修改的前提下擴展。

單一職責原則

一個類為什么要只負責一項職責呢?因為如果一個類承擔的職責過多,就等于把這些職責耦合在一起,當其中一個職責發生變化時,會造成意想不到的破壞。單一職責并不是說一個類只有一個函數,而是說這個類中的函數都是高度相關的,也就是高內聚。單一職責原則關注的是職責,最難的也是職責的劃分,上圖中的發郵件和發短信其實放到一起也是可以的,這兩個行為是否相關的界定是需要根據項際目的實情況來判斷的。

里氏替換原則

父類能出現的地方都可以替換為子類,換句話說,子類可以擴展父類功能,但不能改變父類原有功能。

public class Father {
    public void sendEmail(){
        //發送了郵件
    }
}
public class Son extends Father{
    @Override
    public void sendEmail(){
        //發送了短信
    }
}

上面這個例子中,父類定義了發郵件的方法,但是子類卻發了短信,父類和子類的行為不一致,明顯違背了里氏替換原則。父類的sendEmail方法設定了規范,子類不能任意修改。

依賴倒置原則

依賴倒置原則的解釋是抽象不應該依賴細節,細節應該依賴于抽象,通俗的說,就是面向接口編程而不是面向實現。舉個小明開槍射擊的例子。

//手槍
class Pistol{  
    public void fire(){  
        //單發射擊
    }  
}
//小明
class Xiaoming{  
    public void shoot(Pistol pistol){  
        pistol.fire();  
    }  
}  
//場景類
public class Client{  
    public static void main(String[] args){  
        Xiaoming xiaoming = new Xiaoming();  
        xiaoming.shoot(new Pistol());
    }  
}

小明用手槍射擊沒有問題,但是有一天,需求改成讓小明用步槍射擊怎么辦?

//增加一個步槍類
class Rifle{  
    public void fire(){  
        //連發射擊
    }  
}
//修改小明的射擊方法
class Xiaoming{  
    public void shoot(Rifle rifle){  
        rifle.fire();  
    }  
}  
//修改場景類
public class Client{  
    public static void main(String[] args){  
        Xiaoming xiaoming = new Xiaoming();  
        xiaoming.shoot(new Rifle());
    }  
}

因為手槍和小明是強耦合關系,所以換成步槍必須修改小明類和場景類才行,以后換成其他槍,還是要不斷修改,這顯然不是個好設計。如何降低手槍和小明的耦合呢?添加一個抽象接口不就行了嘛!改造后如下:

//抽象一個槍的接口
interface IGUN{  
    void fire();  
}
//手槍
class Pistol implements IGUN{  
    public void fire(){  
        //單發射擊
    }  
}
//步槍類
class Rifle implements IGUN{  
    public void fire(){  
        //連發射擊
    }  
}
//小明
class Xiaoming{  
    public void shoot(IGUN gun){  
        rifle.fire();  
    }  
}  
//場景類
public class Client{  
    public static void main(String[] args){  
        Xiaoming xiaoming = new Xiaoming();  
        //用手槍射擊
        xiaoming.shoot(new Pistol());
        //用步槍射擊
        xiaoming.shoot(new Rifle());
    }  
}

這樣是不是就好多了,以后需求再讓小明用別的槍,就不用那么麻煩了。

接口隔離原則

接口隔離原則是說要避免臃腫的接口,盡量細化接口,其實也是為了解耦。舉例說明一下:
首先,定義一個功能聚合的(臃腫的)接口

//各種功能定義到一個接口中
interface IFuns{  
    void playAudio();  //播放音頻
    void playVideo();  //播放視頻
    void readBook();  //閱讀書籍
    void readComic();  //瀏覽漫畫
    //...
}

現在要生產兩個產品,一個是mp4,一個是kindle,代碼如下

//mp4目前只能播放音頻和視頻
class Mp4 implements IFuns{  
    public void fire(){  
        public void playAudio(){
            //播放音頻
        };
        public void playVideo(){
            //播放視頻
        };
        public void readBook(){
            //不支持
        };
        public void readComic(){
            //不支持
        };
    }  
}
//kindle的設計只是為了閱讀書籍和瀏覽漫畫
class Kindle implements IFuns{  
    public void fire(){  
        public void playAudio(){
            //不支持
        };
        public void playVideo(){
            //不支持
        };
        public void readBook(){
            //閱讀書籍
        };
        public void readComic(){
            //瀏覽漫畫
        };
    }  
}

要實例化產品出來,每個產品必須實現IFuns中所有的功能,但是呢,對于mp4,目前只研發出了看視頻和音頻的功能,其他功能還在研發中,對于kindle,這個產品是專注于閱讀書籍和瀏覽漫畫的,不想具備其他功能?,F在的設計要求必須實現產品不需要的功能,這是不滿足需求的。于是IFuns接口被拆分為:

interface IFunsA{  
    void playAudio();  //播放音頻
    void playVideo();  //播放視頻
}
interface IFunsB{  
    void readBook();  //閱讀書籍
    void readComic();  //瀏覽漫畫
}
//Mp4實現IFunsA;Kindle實現IFunsB 代碼省略

這樣拆分滿足了kindle專注于閱讀書籍和瀏覽漫畫而不想具備其他功能。但是呢,對于Mp4來說,并不是不想具備其他功能,只是現在還沒研發出來而已,以后mp4還是需要實現閱讀書籍和瀏覽漫畫的。于是還需要進行拆分:

interface IFunsA{  
    void playAudio();  //播放音頻
}
interface IFunsB{  
    void playVideo();  //播放視頻
}
interface IFunsC{  
    void readBook();  //閱讀書籍
}
interface IFunsD{  
    void readComic();  //瀏覽漫畫
}
//Mp4實現IFunsA,IFunsB;Kindle實現IFunsC,IFunsD 代碼省略

這樣拆分后,產品需要什么功能,實現對應的接口就行了。
這個結果看上去很像單一職責原則?。侩m然看起來差不來,其實是不一樣的。單一職責原則注重的是職責,與接口中的方法數量無關,而接口隔離原則注重于對接口的解耦,接口隔離是在滿足單一職責的基礎上,盡可能減少方法。

迪米特法則

又稱為最少知道原則。定義是一個對象應當對其他對象有盡可能少的了解,不和陌生人說話。通俗的說就是一個類對于它需要調用的類,知道的越少越好,如果知道的越多,類之間的關系就會越密切,耦合度就會變得越來越高,耦合度高了,類的復用率就降低了。
有篇文章對迪米特法則解釋很好,可以點進去看看

開閉原則

軟件實體應當在不修改的前提下擴展,就是說,需求變化時,不要修改已有的穩定的代碼,可以通過擴展的方式滿足需求,以避免引入新的bug。

這個原則感覺沒什么好解釋的了,因為前面5個原則遵守好了,設計出來的軟件就是符合開閉原則的。換句話說,開閉原則是其他5個原則的抽象。

軟件設計最大的難題就是應對需求的不斷變化,開閉原則應該說是軟件設計的最終目標了。

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

推薦閱讀更多精彩內容

  • 目錄: 設計模式六大原則(1):單一職責原則 設計模式六大原則(2):里氏替換原則 設計模式六大原則(3):依賴倒...
    加油小杜閱讀 739評論 0 1
  • 設計模式6大原則 轉自:http://www.cnblogs.com/devinzhang/archive/201...
    犀利的小眼神閱讀 440評論 0 1
  • 程序設計的6大原則: 單一職責原則里氏替換原則依賴倒置原則接口隔離原則迪米特法則開閉原則 從根本學好,理解為什么要...
    silencefun閱讀 2,428評論 1 4
  • 設計模式六大原則 開閉原則 開閉原則,是說對于軟件實體(類、模塊、函數等等)應該可以拓展,但是不可修改 這句話有兩...
    ChaLLengerZeng閱讀 908評論 0 0
  • 再過13天也就是春節來臨了,也意味著真正2017年已度過,迎來2018年,2017年太多東西也失去過,握...
    MrLi2017閱讀 264評論 0 0