設計模式總篇:從爲什麼需要原則到實際落地(附知識圖譜)

聊聊爲什麼需要原則

我們所有人都看過科幻電影,都看到過未來場景中人類和機器人和平相處的場景

爲了讓擁有自主智能的機器人不失控,人類爲機器人制定了三大定律:

  • 第一定律:機器人不得傷害人類個體,或者目睹人類個體將遭受危險而袖手不管
  • 第二定律:機器人必須服從人給予它的命令,當該命令與第一定律衝突時例外
  • 第三定律:機器人在不違反第一、第二定律的情況下要儘可能保護自己的生存

當然有時也會出現下面的情況,機器人和人類開始互爲陣營,各自爲敵

但是各自爲敵的情況出現,一般都是機器人覺醒了自我意識,不再遵守三大定律

從邏輯學來說,如果機器人完全遵守三大定律及其衍生的條約,那麼機器人就可以和人類和平相處,當然也會有意外發生。

寫代碼爲什麼需要設計原則

和機器人的三大定律相仿,幾十年的編程經驗,讓幾代人總結出來了一些代碼設計上的定律,這就是設計模式的七大原則

我們遵循七大原則,一定會寫出最完美的代碼嗎?

答案當然是不一定,畢竟沒有人能保證自己可以完全遵循七大原則,同時個人的編程能力也會起到決定性因素。

那我們爲什麼還要遵守?

我自己想到的一句名言(以後或許可以成爲名言~)

向着最好的方向去努力,總不會是最差的結果。

七大原則詳解

開閉原則 ★★★★★

軟件實體對擴展是開放的,但對修改是關閉的,即在不修改一個軟件實體的基礎上去擴展其功能

例如:

以策略模式爲例,當我們新增一種策略的時候,只需要實現策略頂層接口,在調用的時指向新的策略即可

針對這一條原則,在實現難度上要比單一職責更難,在編碼期間,我們需要充分考慮未來的拓展性,規範接口,依賴抽象,這樣才能在需要拓展的時候,非常方便的實現其效果

最佳實踐案例:【一起學系列】之模板方法:寫SSO我只要5分鐘

說明:在接入第三方SSO時,如果需要新增接入方,基於文中的案例,只需實現固定接口,即可優雅的實現相應需求

依賴倒置原則 ★★★★★

要針對抽象層編程,而不要針對具體類編程

例如:

以適配器模式爲例,將一個類的接口轉換成客戶希望的另外一個接口,以此實現的前提便是代碼中所依賴的都是抽象的,因爲只有依賴抽象,才能在代碼運行期間改變其實體,利用多態實現需要的效果

針對該條原則,其實有一定編程經驗的人一定會在無形中注意到,而且瞭解設計模式的話,會發現所有涉及接口和實現的設計模式都會遵從這一條原則

最佳實踐案例:【一起學系列】之模板方法:寫SSO我只要5分鐘

說明:和上一條原則的側重點不同,在SSO中必然有其固定的流程,如登錄-獲取Token-獲取用戶信息-解析-退出等等,在代碼的編寫階段,需要我們定義出接口/抽象類,然後依賴於抽象層,最終改變具體類,以此達到無縫切換的效果

合成複用原則 ★★★★☆

總結一句話就是:多用組合,少用繼承

例如:

以單例模式和代理模式爲例,它們都是該模式的最佳實踐者,單例模式是把不同的策略接口通過組合的方式嵌入到Context類中,如代碼所示:

public abstract class Duck {
    /**
     * 飛行行爲是動態的,可能會變的,因此抽成多個接口的組合,而不是讓Duck類繼承
     */
    FlyBehavior flyBehavior;

    /**
     * 每個鴨子的叫聲不同,抽象成接口
     */
    QuackBehavior quackBehavior;
}

同理,代理模式也是如此,這裏就考慮到一個問題,爲什麼要多用組合而非繼承?

其實還是Java中單繼承引發的問題,同時繼承的語義過於苛刻,因此更多的時候建議善用組合

最佳實踐案例:【一起學系列】之策略模式:好多鴨子啊

說明:策略模式就是合成複用原則的最佳實踐者,沒有之一

單一職責原則 ★★★★☆

類的職責要單一,不能將太多的職責放在一個類中

例如:

在代碼設計中某種場景可能存在多種不同的狀態,很可能就把代碼混在一起了,這時我們利用狀態模式進行設計,把各種狀態對應的實現細節都用類的級別單獨劃分,即體現了單一職則原則

針對這一條原則,其實絕大多數人在設計之初都會考慮到,但問題就在於隨着工作中人員職責的交叉,很有可能會破壞他人設計的最初目的,爲了方便,讓一個類擁有五花八門的功能

最佳實踐案例:【一起學系列】之狀態模式:你聽過“流程”模式嗎?

說明:在狀態模式中,每一種狀態的處理都是獨立的一個類,每個類只需要處理自身的核心邏輯,完美體現了單一職責原則

里氏代換原則 ★★★★☆

在軟件系統中,一個可以接受基類對象的地方必然可以接受一個子類對象

當使用繼承時,遵循里氏替換原則。類B繼承類A時,除添加新的方法完成新增功能外,儘量不要重寫父類A的方法,也儘量不要重載父類A的方法

繼承包含這樣一層含義:父類中凡是已經實現好的方法(相對於抽象方法而言),實際上是在設定一系列的規範和契約,雖然它不強制要求所有的子類必須遵從這些契約,但是如果子類對這些非抽象方法任意修改,就會對整個繼承體系造成破壞。而里氏替換原則就是表達了這一層含義

例如:

我們都用過ArrayList,有誰看過 forEach方法的源碼?

// ArrayList 的父級接口 Iterable  定義的默認方法
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

ArrayList重寫的方法:

@Override
public void forEach(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    @SuppressWarnings("unchecked")
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        action.accept(elementData[i]);
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

我們看到,ArrayList的重寫只是針對數組這種結構優化了性能,其目的性和Iterable接口中的完全一致,因此這種方式的重寫不會引起任何問題,反而可以提高效率,我們需要學習這樣的方式

迪米特原則 ★★★☆☆

一個對象應該對其他對象保持最少的瞭解,又名:最少知道原則

例如:

在代碼設計場景中,某一個類的調用都會固定使用三個方法,是否可以考慮把三個方法抽取出來,提供一個公共的對外方法?這種思路就是外觀模式,外觀模式也是迪米特原則的最佳實踐

最佳實踐案例:【一起學系列】之適配器模式:還有外觀模式呢

說明:利用外觀模式構建統一的對外方法,屏蔽其內部實現,這樣一旦內部實現需要更改,完全不會影響調用方,你Get了嗎?

接口隔離原則 ★★☆☆☆

使用多個專門的接口來取代一個統一的接口

這個模式其實也很好理解,比如我們定義了接口A,接口B實現了接口A,接口C實現了接口B,基類D其實只需要接口C的方法,但是此時不得不實現所有的方法

其實造成這個根本原因:對接口的抽象,設計出現了偏差

畢竟看過JDK源碼或者Spring源碼的同學,可以經常發現某一個接口可能實現了一大堆的接口,但是對於普通開發者而言,沒有這種強大的設計能力,就需要在設計的時候多思考,如果發現違背了接口隔離原則的情況,就應該對接口進行拆分

思維導圖

Processon分享地址:https://www.processon.com/view/link/5f0210aa7d9c0844204b4845

PS:需要源文件的小夥伴可以在文末掃描二維碼,發送【設計模式】即可

設計模式的系列文章推薦

所屬類型 設計模式 標題 & 鏈接
行爲型模式 策略模式 【一起學系列】之策略模式:好多鴨子啊
行爲型模式 觀察者模式 【一起學系列】之觀察者模式:我沒有在監控你啊
行爲型模式 命令模式 【一起學系列】之命令模式:封裝一個簡單Jedis?
行爲型模式 模板方法模式 【一起學系列】之模板方法:寫SSO我只要5分鐘
行爲型模式 迭代器模式 【一起學系列】之迭代器&組合:雖然有點用不上啦
行爲型模式 狀態模式 【一起學系列】之狀態模式:你聽過“流程”模式嗎?
行爲型模式 職責鏈模式 【一起學系列】之剩下的設計模式們
行爲型模式 備忘錄模式 【一起學系列】之剩下的設計模式們
結構型模式 裝飾器模式 【一起學系列】之裝飾器模式:不改代碼增強功能?
結構型模式 適配器模式 & 外觀模式 【一起學系列】之適配器模式:還有外觀模式呢
結構型模式 組合模式 【一起學系列】之迭代器&組合:雖然有點用不上啦
結構型模式 代理模式 【一起學系列】之代理模式:是爲了控制訪問啊!
創建型模式 工廠模式
(工廠方法及抽象工廠)
【一起學系列】之工廠模式:產品?產品族?
創建型模式 單例模式 【一起學系列】之單例模式:只推薦三種~
創建型模式 建造者模式 【一起學系列】之剩下的設計模式們

源碼鏈接

GitHub地址

  • 兼顧了《HeadFirst》以及《GOF》兩本經典書籍中的案例
  • 提供了友好的閱讀指導
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章