設計模式原則(下)

參考鏈接:https://www.imooc.com/read/53/article/1079

1、基本概念

本節繼續介紹設計模式的七大原則的基本概念,上一節重要講了開閉原則、單一職責原則、里氏替換原則、依賴倒置原則,這一節我們主要了解下接口隔離原則、迪米特法則以及合成複用原則。

本節以介紹基本概念爲主,其中會加入部分演示代碼、uml 類圖講解,能理解基本概念即可。後續章節設計模式的講解會詳細介紹這些原則的應用。

本節主要內容有:

  • 什麼是接口隔離原則、迪米特法則以及合成複用原則

  • 爲何要遵循這些原則

2、接口隔離原則

接口隔離原則(Interface Segregation Principle,ISP)的定義是客戶端不應該依賴它不需要的接口,類間的依賴關係應該建立在最小的接口上。 簡單來說就是建立單一的接口,不要建立臃腫龐大的接口。也就是接口儘量細化,同時接口中的方法儘量少,保持接口純潔性。

我們所講的接口主要分爲兩大類,一是實例接口,比如使用 new 關鍵字產生一種實例,被 new 的類就是實例類的接口。從這個角度出發的話,java 中的類其實也是一種接口。二是類接口,java 中常常使用 interface 關鍵字定義。

舉個栗子來說,我們使用接口 IPrettyGirl 來描述美女,剛開始類圖可能描述如下:

圖片描述
但是發現該接口中包含對美女的外觀描述、內在美描述等,幾乎將美女的所有特性全部納入,這顯然不是一個很好的設計規範,比如在唐朝,在那個以豐腴爲美的時代對美的理解就不同,就會出現單純 goodLooking 過關就是美女的結果,所以這裏我們需要將接口隔離拆分。將一個接口拓展爲兩個,增加系統靈活性及可維護性。圖片描述
這裏我們將美女接口拆分爲內在美、外在美兩個接口,系統靈活性提高了,另外接口間還能使用繼承實現聚合,系統拓展性也得到了增強。

接口隔離原則總結:

  • 接口儘量粒度化,保持接口純潔性

  • 接口要高內聚,即減少對外交互

3、迪米特法則

迪米特法則(Law of Demeter,LOD),有時候也叫做最少知識原則(Least Knowledge Principle,LKP),它的定義是:一個軟件實體應當儘可能少地與其它實體發生相互作用。迪米特法則的初衷在於降低類之間的耦合。

舉個栗子,拿教師點名來講,體育老師需要清點班上學生人數,教師一般不是自己親自去數,而是委託組長或班長等人去清點,即教師通過下達命令至班長要求清點人數:

public class Girl {

}

public class GroupLeader {

    private final List<Girl> girls;

    public GroupLeader(List<Girl> girls) {
        this.girls = girls;
    }

    public void countGirls() {
        System.out.println("The sum of girls is " + girls.size());
    }
}

public class Teacher {

    public void command(GroupLeader leader){
        leader.countGirls();
    }
}

public class Main {

    public static void main(String[] args) throws Exception {
        Teacher teacher = new Teacher();
        GroupLeader groupLeader = new GroupLeader(Arrays.asList(new Girl(), new Girl()));
        teacher.command(groupLeader);
    }
}

圖片描述

上述例子中,如果去掉 GroupLeader 這個中間人角色,教師就會直接去清點人數,這樣做會違反迪米特法則。

迪米特法則總結:

  • 類定義時儘量內斂,少使用 public 權限修飾符,儘量使用 private、protected 等。

4、合成複用原則

合成複用原則是通過將已有的對象納入新對象中,作爲新對象的成員對象來實現的,新對象可以調用已有對象的功能,從而達到複用。 原則是儘量首先使用合成 / 聚合的方式,而不是使用繼承。

合成和聚合都是關聯的特殊種類。合成是值的聚合(Aggregation by Value),而複合是引用的聚合(Aggregation by Reference)。

都知道,類之間有三種基本關係,分別是:關聯(聚合和組合)、泛化(與繼承同一概念)、依賴。

這裏我們提一下關聯關係,客觀來講,大千世界中的兩個實體之間總是有着千絲萬縷的關係,歸納到軟件系統中就是兩個類之間必然存在關聯關係。如果一個類單向依賴另一個類,那麼它們之間就是單向關聯。如果彼此依賴,則爲相互依賴,即雙向關聯。

關聯關係包括兩種特例:聚合和組合。聚合,用來表示整體與部分的關係或者 “擁有” 關係。其中,代表部分的對象可能會被代表多個整體的對象所擁有,但是並不一定會隨着整體對象的銷燬而銷燬,部分的生命週期可能會超越整體。好比班級和學生,班級銷燬或解散後學生還是存在的,學生可以繼續存在某個培訓機構或步入社會,生命週期不同於班級甚至大於班級。

合成,用來表示一種強得多的 “擁有” 關係。其中,部分和整體的生命週期是一致的,一個合成的新的對象完全擁有對其組成部分的支配權,包括創建和泯滅。好比人的各個器官組成人一樣,一旦某個器官衰竭,人也不復存在,這是一種 “強” 關聯。

圖片描述

如上圖所示,Heart 和 Student、Teacher 都是一種 “強” 關聯,人不能擺脫心臟而存在,即組合關係,而 Student 和 Class、School 是一種 “弱” 關聯,脫離了學校、班級,學生還能屬於社會或其他團體,即聚合關係。

合成複用原則總結:

  • 新對象可以調用已有對象的功能,從而達到對象複用

5、總結

總結下這兩節的內容,我們一共介紹了 7 種設計原則,它們分別爲開閉原則、單一職責原則、里氏替換原則、依賴倒置原則、接口隔離原則和本節所介紹的合成複用原則。

各種原則要求的側重點不同,總地來說:

1、開閉原則是核心,對拓展開放對修改關閉是軟件設計、後期拓展的基石;
2、單一職責原則就要求我們設計接口,制定模塊功能時保持模塊或者接口功能單一,接口設計或功能設計儘量保持原子性,修改一處不能影響全局或其它模塊;
3、里氏替換原則和依賴倒置原則,按照作者的理解,這倆原則總的是要求我們要面向接口、面向抽象編程,設計程序的時候儘可能使用基類或者接口進行對象的定義或引用,而不是具體的實現,否則實現一旦有變更,上層調用者就必須做出對應變更,這樣一來,整個模塊可能都需要重新調整,非常不利於後期拓展。
圖片描述
4、接口隔離原則具體應用到程序中,比如我們在傳統 mvc 開發時,service 層調用 dao 層一般會使用接口進行調用,各層之間儘量面向接口通信,其實也是一種降低模塊耦合的方法;
5、迪米特法則的初衷也是爲了降低模塊耦合,代碼示例中我們引入了類似 “中間人” 的概念,上層模塊不直接調用下層模塊,而是引入第三方進行代辦,這也是爲了降低模塊的耦合度;
6、合成複用原則一節,我們介紹了聚合、組合的概念,聚合是一種弱關聯,而組合是一種強關聯,表現在 UML 類圖上的話聚合是使用空心四邊形加箭頭表示,而組合是使用實心四邊形加箭頭表示,合成複用原則總的就是要求我們儘量利用好已有對象,從而達到功能複用,具體是聚合還是組合,還是一般關聯,就要看具體情況再定了。

這 7 種設計原則是軟件設計必須儘量遵循的原則。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章