Java 中的中介模式(Mediator Pattern)

  • 2019 年 12 月 5 日
  • 筆記

1. 概述

在本文中,我們將介紹一種 GoF 行為模式——中介模式。我們將描述它的目的並說明何時應該使用它。

和往常一樣,我們還提供簡單的示例程式碼。

2. 中介模式

在面向對象編程中,我們應該總是嘗試組件是松耦合和可復用的方式設計系統。這種方法使我們的程式碼更易於維護和測試。

然而,在現實生活中,我們經常需要處理一組複雜的依賴對象。這時中介模式會派上用場。

中給模式的目的是降低緊密耦合對象之間直接和其他對象通訊的複雜性和依賴性。這是通過創建一個中介對象來實現的,該對象負責依賴對象之間的交互。因此,所有的通訊都通過中介。

這促進了松耦合,因為一組組件一起工作不再需要直接交互。相反,他們只引用獨立的中介對象。這樣,在系統的其他部分中復用這些對象也更容易。

3. 中介模式的 UML 圖

現在讓我們直觀的看看這個模式:

在上面的 UML 圖中,我們可以識別一下參與者:

  • Mediator 定義了 Colleague 對象用於通訊的介面
  • Colleague 定義了包含 Mediator 的單個引用的抽象類
  • ConcreteMediator 封裝了 Colleague 對象之間的交互邏輯
  • ConcreteColleague1 和 ConcreteColleague2 只通過 Mediator 通訊

我們可以看到,Colleague 對象不直接相互引用。相反的,所有通訊都通過 Mediator 執行。

因此,ConcreteColleague1 和 ConcreteColleague2 可以更容易的復用。

另外,假如我們需要改變 Colleague 一起工作的方式,我們只需要修改 ConcreteMediator 的邏輯。或者我們可以創建新的 Mediator 的實現。

4. Java 實現

現在我們對這個理論有了清晰得認識,那麼讓我們看一個例子,在實踐中更好得理解這個概念。

4.1. 示例場景

想像一下,我們正在構建一個簡單的冷卻系統,由風扇、電源和按鈕組成。按下按鈕將打開或關閉風扇。在我們打開風扇之前,我們需要打開電源。同樣,我們必須在風扇關閉之後立即關閉電源。

現在讓我們看一下示例實現:

    public class Button {            private Fan fan;              // constructor, getters and setters              public void press(){                if(fan.isOn()){                    fan.turnOff();                } else {                    fan.turnOn();                }            }        }        public class Fan {            private Button button;            private PowerSupplier powerSupplier;            private boolean isOn = false;              // constructor, getters and setters              public void turnOn() {                powerSupplier.turnOn();                isOn = true;            }              public void turnOff() {                isOn = false;                powerSupplier.turnOff();            }        }        public class PowerSupplier {            public void turnOn() {                // implementation            }              public void turnOff() {                // implementation            }        }

接下來,讓我們測試一下這個功能:

    @Test        public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() {            assertFalse(fan.isOn());              button.press();            assertTrue(fan.isOn());              button.press();            assertFalse(fan.isOn());        }

一切看起來運行正常。但請注意Button、Fan 和 PowerSupplier 類是緊耦合的。Button 直接操作 Fan,Fan 與 Button 和 PowerSupplier 都進行交互。

在其他模組中復用 Button 類會很困難。此外,如果我們需要在系統中添加第二個電源,那麼我們必須修改 Fan 類的邏輯。

4.2. 添加中介模式

現在,讓我們實現中介模式以減少我們類之間的依賴,是程式碼更具可復用性。

首先,讓我們介紹 Mediator 類:

    public class Mediator {            private Button button;            private Fan fan;            private PowerSupplier powerSupplier;              // constructor, getters and setters              public void press() {                if (fan.isOn()) {                    fan.turnOff();                } else {                    fan.turnOn();                }            }              public void start() {                powerSupplier.turnOn();            }              public void stop() {                powerSupplier.turnOff();            }        }

接下來,讓我們修改剩下的類:

    public class Button {            private Mediator mediator;              // constructor, getters and setters              public void press() {                mediator.press();            }        }        public class Fan {            private Mediator mediator;            private boolean isOn = false;              // constructor, getters and setters              public void turnOn() {                mediator.start();                isOn = true;            }              public void turnOff() {                isOn = false;                mediator.stop();            }        }

讓我們再一次測試這個功能:

    @Test        public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() {            assertFalse(fan.isOn());              button.press();            assertTrue(fan.isOn());              button.press();            assertFalse(fan.isOn());        }

我們的冷卻系統按預期工作。

現在我們已經實現了中介模式,Button、Fan 或 PowerSupplier 類都沒有直接通訊。他們只有一個對 Mediator 的引用。

如果我們將來需要添加第二個電源,我們需要做的只是更新 Mediator 的邏輯;Button 和 Fan 類保持不變。

這個示例展示了我們可以輕鬆的分離依賴的對象並使我們的系統更易於維護。

5. 何時使用中介模式

如果我們必須處理一組緊耦合且難以維護的對象時,中介模式是一個不錯的選擇。這樣我們可以減少對象間的依賴並降低整體複雜性。

此外,通過使用中介對象,我們將通訊邏輯提取到單個組件,這樣,我們遵循了單一職責原則。此外,我們可以引入新的中介而不需要更改系統的其餘部分。因此,我們遵循開閉原則(譯者註:對擴展開放,對修改封閉)。

然而,有時由於系統的錯誤設計,我們可能會由太多緊耦合對象。如果是這種情況,我們不應該應用中介模式。相反的,我們應該後退一步,重新思考對類建模的方式。

和所有其他模式一樣,我們需要在盲目實現中介模式前考慮我們的特殊用例。

6. 總結

在本文中,我們了解了中介模式。我們說明了這個模式解決了什麼問題以及我們什麼時候應該實際考慮使用它。我們還實現了這個設計模式的一個簡單示例。