【不失業計劃】設計模式 part3一行爲型模式

轉載說明:資料來源 https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/behavioral.html 主要作爲個人學習查閱資料,需要刪除請聯繫

附另一直觀設計模式學習網站:https://refactoringguru.cn/design-patterns

三、行爲型模式

1、命令模式

定義

命令模式是一種行爲設計模式, 它可將請求轉換爲一個包含與請求相關的所有信息的獨立對象。 該轉換讓你能根據不同的請求將方法參數化、 延遲請求執行或將其放入隊列中, 且能實現可撤銷操作。

類比於

在市中心逛了很久的街後, 你找到了一家不錯的餐廳, 坐在了臨窗的座位上。 一名友善的服務員走近你, 迅速記下你點的食物, 寫在一張紙上。 服務員來到廚房, 把訂單貼在牆上。 過了一段時間, 廚師拿到了訂單, 他根據訂單來準備食物。 廚師將做好的食物和訂單一起放在托盤上。 服務員看到托盤後對訂單進行檢查, 確保所有食物都是你要的, 然後將食物放到了你的桌上。

那張紙就是一個命令, 它在廚師開始烹飪前一直位於隊列中。 命令中包含與烹飪這些食物相關的所有信息。 廚師能夠根據它馬上開始烹飪, 而無需跑來直接和你確認訂單詳情。

在這裏插入圖片描述

結構

  • Command: 抽象命令類

  • ConcreteCommand: 具體命令類

  • Invoker: 調用者

  • Receiver: 接收者

  • Client:客戶類

在這裏插入圖片描述

時序圖

在這裏插入圖片描述

優點

  • 單一職責原則。 你可以解耦觸發和執行操作的類。
  • 開閉原則。 你可以在不修改已有客戶端代碼的情況下在程序中創建新的命令。
  • 你可以實現撤銷和恢復功能。
  • 你可以實現操作的延遲執行。
  • 你可以將一組簡單命令組合成一個複雜命令。

缺點

  • 代碼可能會變得更加複雜, 因爲你在發送者和接收者之間增加了一個全新的層次。

使用場景

  • **如果你需要通過操作來參數化對象, ** 可使用命令模式

    命令模式可將特定的方法調用轉化爲獨立對象。 這一改變也帶來了許多有趣的應用: 你可以將命令作爲方法的參數進行傳遞、 將命令保存在其他對象中, 或者在運行時切換已連接的命令等。

    舉個例子: 你正在開發一個 GUI 組件 (例如上下文菜單), 你希望用戶能夠配置菜單項, 並在點擊菜單項時觸發操作。

  • 如果你想要將操作放入隊列中、 操作的執行或者遠程執行操作, 可使用命令模式。

    同其他對象一樣, 命令也可以實現序列化 (序列化的意思是轉化爲字符串), 從而能方便地寫入文件或數據庫中。 一段時間後, 該字符串可被恢復成爲最初的命令對象。 因此, 你可以延遲或計劃命令的執行。 但其功能遠不止如此! 使用同樣的方式, 你還可以將命令放入隊列、 記錄命令或者通過網絡發送命令。

  • 如果你想要實現操作回滾功能可使用命令模式

總結

  • 在命令模式中,將一個請求封裝爲一個對象,從而使我們可用不同的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操作。命令模式是一種對象行爲型模式,其別名爲動作模式或事務模式。
  • 命令模式包含四個角色:抽象命令類中聲明瞭用於執行請求的execute()等方法,通過這些方法可以調用請求接收者的相關操作;具體命令類是抽象命令類的子類,實現了在抽象命令類中聲明的方法,它對應具體的接收者對象,將接收者對象的動作綁定其中;調用者即請求的發送者,又稱爲請求者,它通過命令對象來執行請求;接收者執行與請求相關的操作,它具體實現對請求的業務處理。
  • 命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開。命令模式使請求本身成爲一個對象,這個對象和其他對象一樣可以被存儲和傳遞。
  • 命令模式的主要優點在於降低系統的耦合度,增加新的命令很方便,而且可以比較容易地設計一個命令隊列和宏命令,並方便地實現對請求的撤銷和恢復;其主要缺點在於可能會導致某些系統有過多的具體命令類。
  • 命令模式適用情況包括:需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互;需要在不同的時間指定請求、將請求排隊和執行請求;需要支持命令的撤銷操作和恢復操作,需要將一組操作組合在一起,即支持宏命令。

2、中介者模式

定義

中介者模式是一種行爲設計模式, 能讓你減少對象之間混亂無序的依賴關係。 該模式會限制對象之間的直接交互, 迫使它們通過一箇中介者對象進行合作。

類比於

飛行器駕駛員之間不會通過相互溝通來決定下一架降落的飛機。 所有溝通都通過控制塔臺進行。

在這裏插入圖片描述

中介者模式可以使對象之間的關係數量急劇減少。

中介者承擔兩方面的職責:

  • 中轉作用(結構性):通過中介者提供的中轉作用,各個同事對象就不再需要顯式引用其他同事,當需要和其他同事進行通信時,通過中介者即可。該中轉作用屬於中介者在結構上的支持。
  • 協調作用(行爲性):中介者可以更進一步的對同事之間的關係進行封裝,同事可以一致地和中介者進行交互,而不需要指明中介者需要具體怎麼做,中介者根據封裝在自身內部的協調邏輯,對同事的請求進行進一步處理,將同事成員之間的關係行爲進行分離和封裝。該協調作用屬於中介者在行爲上的支持。

結構

  • Mediator: 抽象中介者

  • ConcreteMediator: 具體中介者

  • Colleague: 抽象同事類

  • ConcreteColleague: 具體同事類
    在這裏插入圖片描述

時序圖

在這裏插入圖片描述

優點

  • 單一職責原則。 你可以將多個組件間的交流抽取到同一位置, 使其更易於理解和維護。
  • 開閉原則。 你無需修改實際組件就能增加新的中介者。
  • 你可以減輕應用中多個組件間的耦合情況。
  • 你可以更方便地複用各個組件。

缺點

  • 一段時間後, 中介者可能會演化成爲上帝對象
  • 在具體中介者類中包含了同事之間的交互細節,可能會導致具體中介者類非常複雜,使得系統難以維護。

適用場景

  • **當一些對象和其他對象緊密耦合以致難以對其進行修改時, ** 可使用中介者模式

    該模式讓你將對象間的所有關係抽取成爲一個單獨的類, 以使對於特定組件的修改工作獨立於其他組件。

  • 當組件因過於依賴其他組件而無法在不同應用中複用時, 可使用中介者模式。

    應用中介者模式後, 每個組件不再知曉其他組件的情況。 儘管這些組件無法直接交流, 但它們仍可通過中介者對象進行間接交流。 如果你希望在不同應用中複用一個組件, 則需要爲其提供一個新的中介者類。

  • 如果爲了能在不同情景下複用一些基本行爲, 導致你需要被迫創建大量組件子類時, 可使用中介者模式。

    由於所有組件間關係都被包含在中介者中, 因此你無需修改組件就能方便地新建中介者類以定義新的組件合作方式。

總結

  • 中介者模式用一箇中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。中介者模式又稱爲調停者模式,它是一種對象行爲型模式。
  • 中介者模式包含四個角色:抽象中介者用於定義一個接口,該接口用於與各同事對象之間的通信;具體中介者是抽象中介者的子類,通過協調各個同事對象來實現協作行爲,瞭解並維護它的各個同事對象的引用;抽象同事類定義各同事的公有方法;具體同事類是抽象同事類的子類,每一個同事對象都引用一箇中介者對象;每一個同事對象在需要和其他同事對象通信時,先與中介者通信,通過中介者來間接完成與其他同事類的通信;在具體同事類中實現了在抽象同事類中定義的方法。
  • 通過引入中介者對象,可以將系統的網狀結構變成以中介者爲中心的星形結構,中介者承擔了中轉作用和協調作用。中介者類是中介者模式的核心,它對整個系統進行控制和協調,簡化了對象之間的交互,還可以對對象間的交互進行進一步的控制。
  • 中介者模式的主要優點在於簡化了對象之間的交互,將各同事解耦,還可以減少子類生成,對於複雜的對象之間的交互,通過引入中介者,可以簡化各同事類的設計和實現;中介者模式主要缺點在於具體中介者類中包含了同事之間的交互細節,可能會導致具體中介者類非常複雜,使得系統難以維護。
  • 中介者模式適用情況包括:系統中對象之間存在複雜的引用關係,產生的相互依賴關係結構混亂且難以理解;一個對象由於引用了其他很多對象並且直接和這些對象通信,導致難以複用該對象;想通過一箇中間類來封裝多個類中的行爲,而又不想生成太多的子類。

3、觀察者模式

定義

觀察者模式是一種行爲設計模式, 允許你定義一種訂閱機制, 可在對象事件發生時通知多個 「觀察」 該對象的其他對象。

類比於

如果你訂閱了一份雜誌或報紙, 那就不需要再去報攤查詢新出版的刊物了。 出版社 (即應用中的 「發佈者」) 會在刊物出版後 (甚至提前) 直接將最新一期寄送至你的郵箱中。

出版社負責維護訂閱者列表, 瞭解訂閱者對哪些刊物感興趣。 當訂閱者希望出版社停止寄送新一期的雜誌時, 他們可隨時從該列表中退出。
在這裏插入圖片描述

結構

  • Subject: 目標

  • ConcreteSubject: 具體目標

  • Observer: 觀察者

  • ConcreteObserver: 具體觀察者

在這裏插入圖片描述

時序圖

在這裏插入圖片描述

優點

  • 開閉原則。 你無需修改發佈者代碼就能引入新的訂閱者類 (如果是發佈者接口則可輕鬆引入發佈者類)。
  • 你可以在運行時建立對象之間的聯繫。

缺點

  • 訂閱者的通知順序是隨機的。

  • 如果一個觀察目標對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。

  • 如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。

適用場景

  • 當一個對象狀態的改變需要改變其他對象, 或實際對象是事先未知的或動態變化的時, 可使用觀察者模式。

    當你使用圖形用戶界面類時通常會遇到一個問題。 比如, 你創建了自定義按鈕類並允許客戶端在按鈕中注入自定義代碼, 這樣當用戶按下按鈕時就會觸發這些代碼。

    觀察者模式允許任何實現了訂閱者接口的對象訂閱發佈者對象的事件通知。 你可在按鈕中添加訂閱機制, 允許客戶端通過自定義訂閱類注入自定義代碼。

  • 當應用中的一些對象必須觀察其他對象時, 可使用該模式。 但僅能在有限時間內或特定情況下使用。

    訂閱列表是動態的, 因此訂閱者可隨時加入或離開該列表。

總結

  • 觀察者模式定義對象間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知並被自動更新。觀察者模式又叫做發佈-訂閱模式、模型-視圖模式、源-監聽器模式或從屬者模式。觀察者模式是一種對象行爲型模式。
  • 觀察者模式包含四個角色:目標又稱爲主題,它是指被觀察的對象;具體目標是目標類的子類,通常它包含有經常發生改變的數據,當它的狀態發生改變時,向它的各個觀察者發出通知;觀察者將對觀察目標的改變做出反應;在具體觀察者中維護一個指向具體目標對象的引用,它存儲具體觀察者的有關狀態,這些狀態需要和具體目標的狀態保持一致。
  • 觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個目標對象,當這個目標對象的狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新。
  • 觀察者模式的主要優點在於可以實現表示層和數據邏輯層的分離,並在觀察目標和觀察者之間建立一個抽象的耦合,支持廣播通信;其主要缺點在於如果一個觀察目標對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間,而且如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
  • 觀察者模式適用情況包括:一個抽象模型有兩個方面,其中一個方面依賴於另一個方面;一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變;一個對象必須通知其他對象,而並不知道這些對象是誰;需要在系統中創建一個觸發鏈。
  • 在JDK的java.util包中,提供了Observable類以及Observer接口,它們構成了Java語言對觀察者模式的支持。

4、狀態模式

定義

狀態模式是一種行爲設計模式, 讓你能在一個對象的內部狀態變化時改變其行爲, 使其看上去就像改變了自身所屬的類一樣。

類比於

智能手機的按鍵和開關會根據設備當前狀態完成不同行爲:

  • 當手機處於解鎖狀態時, 按下按鍵將執行各種功能。
  • 當手機處於鎖定狀態時, 按下任何按鍵都將解鎖屏幕。
  • 當手機電量不足時, 按下任何按鍵都將顯示充電頁面。

結構

  • Context: 環境類

  • State: 抽象狀態類

  • ConcreteState: 具體狀態類

在這裏插入圖片描述

時序圖

在這裏插入圖片描述

優點

  • 單一職責原則。 將與特定狀態相關的代碼放在單獨的類中。
  • 開閉原則。 無需修改已有狀態類和上下文就能引入新狀態。
  • 通過消除臃腫的狀態機條件語句簡化上下文代碼。

缺點

  • 如果狀態機只有很少的幾個狀態, 或者很少發生改變, 那麼應用該模式可能會顯得小題大作。

適用場景

  • 如果對象需要根據自身當前狀態進行不同行爲, 同時狀態的數量非常多且與狀態相關的代碼會頻繁變更的話, 可使用狀態模式。

    模式建議你將所有特定於狀態的代碼抽取到一組獨立的類中。 這樣一來, 你可以在獨立於其他狀態的情況下添加新狀態或修改已有狀態, 從而減少維護成本。

  • 如果某個類需要根據成員變量的當前值改變自身行爲, 從而需要使用大量的條件語句時, 可使用該模式。

    狀態模式會將這些條件語句的分支抽取到相應狀態類的方法中。 同時, 你還可以清除主要類中與特定狀態相關的臨時成員變量和幫手方法代碼。

  • 當相似狀態和基於條件的狀態機轉換中存在許多重複代碼時, 可使用狀態模式。

    狀態模式讓你能夠生成狀態類層次結構, 通過將公用代碼抽取到抽象基類中來減少重複。

總結

  • 狀態模式允許一個對象在其內部狀態改變時改變它的行爲,對象看起來似乎修改了它的類。其別名爲狀態對象,狀態模式是一種對象行爲型模式。
  • 狀態模式包含三個角色:環境類又稱爲上下文類,它是擁有狀態的對象,在環境類中維護一個抽象狀態類State的實例,這個實例定義當前狀態,在具體實現時,它是一個State子類的對象,可以定義初始狀態;抽象狀態類用於定義一個接口以封裝與環境類的一個特定狀態相關的行爲;具體狀態類是抽象狀態類的子類,每一個子類實現一個與環境類的一個狀態相關的行爲,每一個具體狀態類對應環境的一個具體狀態,不同的具體狀態類其行爲有所不同。
  • 狀態模式描述了對象狀態的變化以及對象如何在每一種狀態下表現出不同的行爲。
  • 狀態模式的主要優點在於封裝了轉換規則,並枚舉可能的狀態,它將所有與某個狀態有關的行爲放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行爲,還可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數;其缺點在於使用狀態模式會增加系統類和對象的個數,且狀態模式的結構與實現都較爲複雜,如果使用不當將導致程序結構和代碼的混亂,對於可以切換狀態的狀態模式不滿足「開閉原則」的要求。
  • 狀態模式適用情況包括:對象的行爲依賴於它的狀態(屬性)並且可以根據它的狀態改變而改變它的相關行爲;代碼中包含大量與對象狀態有關的條件語句,這些條件語句的出現,會導致代碼的可維護性和靈活性變差,不能方便地增加和刪除狀態,使客戶類與類庫之間的耦合增強。

5、策略模式

定義

策略模式是一種行爲設計模式, 它能讓你定義一系列算法, 並將每種算法分別放入獨立的類中, 以使算法的對象能夠相互替換。

類比於

假如你需要前往機場。 你可以選擇乘坐公共汽車、 預約出租車或騎自行車。 這些就是你的出行策略。 你可以根據預算或時間等因素來選擇其中一種策略。
在這裏插入圖片描述

結構

  • Context: 環境類

  • Strategy: 抽象策略類

  • ConcreteStrategy: 具體策略類

在這裏插入圖片描述

時序圖
在這裏插入圖片描述

優點

  • 你可以在運行時切換對象內的算法。
  • 你可以將算法的實現和使用算法的代碼隔離開來。
  • 你可以使用組合來代替繼承。
  • 開閉原則。 你無需對上下文進行修改就能夠引入新的策略。

缺點

  • 客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。
  • 策略模式將造成產生很多策略類,可以通過使用享元模式在一定程度上減少對象的數量。

適用場景

  • 當你想使用對象中各種不同的算法變體, 並希望能在運行時切換算法時, 可使用策略模式。

    策略模式讓你能夠將對象關聯至可以不同方式執行特定子任務的不同子對象, 從而以間接方式在運行時更改對象行爲。

  • 當你有許多僅在執行某些行爲時略有不同的相似類時, 可使用策略模式。

    策略模式讓你能將不同行爲抽取到一個獨立類層次結構中, 並將原始類組合成同一個, 從而減少重複代碼。

  • 如果算法在上下文的邏輯中不是特別重要, 使用該模式能將類的業務邏輯與其算法實現細節隔離開來。

    策略模式讓你能將各種算法的代碼、 內部數據和依賴關係與其他代碼隔離開來。 不同客戶端可通過一個簡單接口執行算法, 並能在運行時進行切換。

  • 當類中使用了複雜條件運算符以在同一算法的不同變體中切換時, 可使用該模式。

    策略模式將所有繼承自同樣接口的算法抽取到獨立類中, 因此不再需要條件語句。 原始對象並不實現所有算法的變體, 而是將執行工作委派給其中的一個獨立算法對象。

總結

  • 在策略模式中定義了一系列算法,將每一個算法封裝起來,並讓它們可以相互替換。策略模式讓算法獨立於使用它的客戶而變化,也稱爲政策模式。策略模式是一種對象行爲型模式。
  • 策略模式包含三個角色:環境類在解決某個問題時可以採用多種策略,在環境類中維護一個對抽象策略類的引用實例;抽象策略類爲所支持的算法聲明瞭抽象方法,是所有策略類的父類;具體策略類實現了在抽象策略類中定義的算法。
  • 策略模式是對算法的封裝,它把算法的責任和算法本身分割開,委派給不同的對象管理。策略模式通常把一個系列的算法封裝到一系列的策略類裏面,作爲一個抽象策略類的子類。
  • 策略模式主要優點在於對「開閉原則」的完美支持,在不修改原有系統的基礎上可以更換算法或者增加新的算法,它很好地管理算法族,提高了代碼的複用性,是一種替換繼承,避免多重條件轉移語句的實現方式;其缺點在於客戶端必須知道所有的策略類,並理解其區別,同時在一定程度上增加了系統中類的個數,可能會存在很多策略類。
  • 策略模式適用情況包括:在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,使用策略模式可以動態地讓一個對象在許多行爲中選擇一種行爲;一個系統需要動態地在幾種算法中選擇一種;避免使用難以維護的多重條件選擇語句;希望在具體策略類中封裝算法和與相關的數據結構。