1. 程式人生 > 實用技巧 >【設計模式】中介者模式

【設計模式】中介者模式

中介者模式

簡介

如果一個系統裡各個物件之間存在多對多的相互關係,可以將物件之間的一些互動行為從各個物件中分離出來,集中封裝在一箇中介者物件中,使其耦合鬆散,並由中介者統一協調。通過中介者,物件之間的多對多關係就簡化了相對更簡單的一對多關係。

中介者模式:定義一個物件來封裝一系列物件的互動。中介者模式使各個物件之間不需要顯示地相互引用,從而使其耦合鬆散,而且使用者可以獨立地改變它們之間的互動。

結構

實現

中介者模式的核心在於引入了中介者類,中介者類承擔了兩個層次的職責:

  • 結構上起中轉作用:通過中介者的中轉,各個同事之間不必再相互呼叫或者引用,只需通過中介者實現間接呼叫的目的。
  • 行為上起協調作用
    :中介者可以進一步地將同事之間的關係進行封裝,同事可以一致地和中介者進行互動,而不必指出中介者具體該如何操作,中介者根據封裝在自身內部的協調邏輯對同事的請求進一步處理,將同事成員之間的關係行為進行分離和封裝。

實現方式:

  • 找到一組當前緊密耦合,且提供其獨立能帶來更大好處的類。
  • 宣告中介者介面並描述中介者和各個元件之間所需的交流介面。
  • 實現具體中介者類。該類可從自行儲存其下所有元件的引用中受益。
  • 可以更進一步,讓中介者負責元件物件的建立和銷燬。此後,中介者可能會與工廠或外觀類似。
  • 元件中必須儲存對於中介者物件的引用。該連線通常在元件的建構函式中建立,該函式會將中介者作為引數傳遞。
  • 修改元件程式碼。使其可呼叫中介者的通知方法,而非其他元件的方法。然後將呼叫其他組建的程式碼抽取到終結者類中,並在中介者接收到該元件通知時執行這些程式碼。
#include <iostream>
#include <string>
#include <memory>


class BaseComponent;

// 中介者
class Mediator {
public:
    virtual ~Mediator() {}
    virtual void Notify(BaseComponent *sender, std::string event) const = 0;
};

// 基礎元件
class BaseComponent {
protected:
    Mediator *m_mediator;

public:
    BaseComponent(Mediator *mediator = nullptr) :m_mediator(mediator) {}
    virtual ~BaseComponent() {}
    void setMediator(Mediator *mediator) {
        this->m_mediator = mediator;
    }
};

// 具體元件
class Component1: public BaseComponent {
public:
    void doA() {
        std::cout << "Component1 doss A.\n";
        this->m_mediator->Notify(this, "A");
    }
    void doB() {
        std::cout << "Component1 doss B.\n";
        this->m_mediator->Notify(this, "B");
    }
};

// 具體元件
class Component2: public BaseComponent {
public:
    void doC() {
        std::cout << "Component1 doss C.\n";
        this->m_mediator->Notify(this, "C");
    }
    void doD() {
        std::cout << "Component1 doss D.\n";
        this->m_mediator->Notify(this, "D");
    }
};

class ConcreteMediator : public Mediator {
private:
    std::shared_ptr<Component1> m_component1;
    std::shared_ptr<Component2> m_component2;

public:
    ConcreteMediator(std::shared_ptr<Component1> component1, std::shared_ptr<Component2> component2) : m_component1(component1), m_component2(component2) {
        this->m_component1->setMediator(this);
        this->m_component2->setMediator(this);
    }
    void Notify(BaseComponent *sender, std::string event) const override {
        if(event == "A") {
            std::cout << "中介者對A做出反應並觸發以下操作:\n";
            this->m_component2->doC();
        }
        if(event == "D") {
            std::cout << "中介者對D做出反應並觸發以下操作:\n";
            this->m_component1->doB();
            this->m_component2->doC();
        }
    }
};


int main(int argc, char * argv[]) {
    std::shared_ptr<Component1> c1 = std::make_shared<Component1>();
    std::shared_ptr<Component2> c2 = std::make_shared<Component2>();
    std::shared_ptr<ConcreteMediator> mediator = std::make_shared<ConcreteMediator>(c1, c2);

    std::cout << "Client:觸發A操作:\n";
    c1->doA();
    std::cout << "\n";

    std::cout << "Client:觸發D操作:\n";
    c2->doD();

    return 0;
}
# -*- coding: utf-8 -*-

from __future__ import annotations
from abc import ABC


class Mediator(ABC):
    """
    """

    def notify(self, sender: object, event: str) -> None:
        pass


class ConcreteMediator(Mediator):
    def __init__(self, component1: Component1, component2: Component2) -> None:
        self._component1 = component1
        self._component1.mediator = self
        self._component2 = component2
        self._component2.mediator = self

    def notify(self, sender: object, event: str) -> None:
        if event == "A":
            print("Mediator reacts on A and triggers following operations:")
            self._component2.do_c()
        elif event == "D":
            print("Mediator reacts on D and triggers following operations:")
            self._component1.do_b()
            self._component2.do_c()


class BaseComponent:
    """
    """

    def __init__(self, mediator: Mediator = None) -> None:
        self._mediator = mediator

    @property
    def mediator(self) -> Mediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: Mediator) -> None:
        self._mediator = mediator


class Component1(BaseComponent):
    def do_a(self) -> None:
        print("Component 1 does A.")
        self.mediator.notify(self, "A")

    def do_b(self) -> None:
        print("Component 1 does B.")
        self.mediator.notify(self, "B")


class Component2(BaseComponent):
    def do_c(self) -> None:
        print("Component 2 does C.")
        self.mediator.notify(self, "C")

    def do_d(self) -> None:
        print("Component 2 does D.")
        self.mediator.notify(self, "D")


if __name__ == "__main__":
    # The client code.
    c1 = Component1()
    c2 = Component2()
    mediator = ConcreteMediator(c1, c2)

    print("Client triggers operation A.")
    c1.do_a()

    print("\n", end="")

    print("Client triggers operation D.")
    c2.do_d()

例項

問題描述

租戶通過中介租房。可以從中介瞭解到房東的資訊(姓名、電話、地址、價格),房東可以從中介註冊自己的房源,同時也可以從中介瞭解到組合的資訊(姓名、電話)。

問題解答

// Example.cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>

enum PERSON_TYPE {
    TENANT,
    LANDLORD
};

class BaseComponent;

class BaseMediator {
public:
    virtual ~BaseMediator() {}
    virtual void registerInfo(BaseComponent *component) = 0;
    virtual void getInfo(BaseComponent *component) = 0;
};

class BaseComponent {
protected:
    BaseMediator *m_mediator;
    PERSON_TYPE personType;

public:
    BaseComponent(BaseMediator *mediator = nullptr) : m_mediator(mediator) {}
    virtual ~BaseComponent() {}
    void setMediator(BaseMediator *mediator) {
        this->m_mediator = mediator;
    }
    virtual void ask() = 0;
    virtual void answer() = 0;
    virtual PERSON_TYPE getType() const = 0;
};

// 租戶
class Tenant: public BaseComponent {
private:
    std::string m_name;
    std::string m_phoneNumber;

public:
    Tenant(const std::string& name = "NULL", const std::string& phoneNumber="NULL") : m_name(name), m_phoneNumber(phoneNumber) {
        personType = TENANT;
    }
    void ask() override {
        std::cout << "租戶檢視房東資訊:\n";
        this->m_mediator->getInfo(this);
    }
    void answer() override {
        std::cout << "[租戶資訊] " << " 姓名:" << m_name << "\t" << " 電話:" << m_phoneNumber  << "\t" << "\n";
    }
    PERSON_TYPE getType() const override {
        return personType;
    }
};

// 房東
class Landlord: public BaseComponent {
private:
    std::string m_name;
    std::string m_phoneNumber;
    std::string m_address;
    std::string m_price;

public:
    Landlord(const std::string& name = "NULL", const std::string& phoneNumber="NULL", const std::string& address="NULL", const std::string& price="NULL") : m_name(name), m_phoneNumber(phoneNumber), m_address(address), m_price(price) {
        personType = LANDLORD;
    }
    void ask() override {
        std::cout << "房東檢視租戶資訊:\n";
        this->m_mediator->getInfo(this);
    }
    void answer() override {
        std::cout << "[房東資訊] " << " 姓名:" << m_name  << "\t" << " 電話:" << m_phoneNumber  << "\t" << " 地址:" << m_address  << "\t" << " 價格:" << m_price  << "\t" << "\n";
    }
    PERSON_TYPE getType() const override {
        return personType;
    }
};

// 中介
class ConcreteMediator: public BaseMediator {
private:
    Tenant *tenant;
    Landlord *landlord;
    std::vector<Tenant*> tenantVector;
    std::vector<Landlord*> landlordVector;

public:
    void registerInfo(BaseComponent* component) override {
        if (component->getType() == TENANT) {
            tenantVector.push_back((Tenant*)component);
        } else if (component->getType() == LANDLORD) {
            landlordVector.push_back((Landlord*)component);
        } else {
            std::cerr << "ERROR\n";
        }
    }
    void getInfo(BaseComponent *component) override {
        if(component->getType() == TENANT) {
            for(const auto &landlord : landlordVector) {
                landlord->answer();
            }

        } else if (component->getType() == LANDLORD) {
            for(const auto &tenant : tenantVector) {
                tenant->answer();
            }
        }
    }
};


int main(int argc, char * argv[]) {
    ConcreteMediator *mediator = new ConcreteMediator;

    Tenant *t1 = new Tenant("張三", "111111");
    Tenant *t2 = new Tenant("李四", "222222");
    t1->setMediator(mediator);
    t2->setMediator(mediator);
    mediator->registerInfo(t1);
    mediator->registerInfo(t2);

    Landlord *l1 = new Landlord("北京房東", "010-241235", "北京市朝陽區", "5500");
    Landlord *l2 = new Landlord("北京房東", "010-3253523", "北京市大興區", "3500");
    Landlord *l3 = new Landlord("重慶房東", "023-3463177", "重慶市沙坪壩區", "1500");
    l1->setMediator(mediator);
    l2->setMediator(mediator);
    l3->setMediator(mediator);
    mediator->registerInfo(l1);
    mediator->registerInfo(l2);
    mediator->registerInfo(l3);

    t1->ask();
    std::cout <<"\n";
    l1->ask();

    delete t1;
    delete t2;
    delete l1;
    delete l2;
    delete l3;

    return 0;
}

總結

優點

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

缺點

  • 一段時間後,中介者可能會演化成為上帝物件
  • 具體中介者類中包含了元件之間互動的細節和邏輯,可能使得中介者類很複雜以至於難以管理維護。

場景

  • 當一些物件和其他物件緊密耦合以至於難以對其進行修改時,可使用中介者模式。
  • 當元件因過於依賴其他元件而無法在不同應用中複用時,可使用中介者模式。
  • 如果為了能在不同情景下複用一些基本行為,導致需要被迫建立大量元件子類時,可使用中介者模式。

與其他模式的關係

  • 責任鏈模式命令模式中介者模式觀察者模式用於處理請求傳送者和接收者之間的不同連線方式:
    • 責任鏈按照順序將請求動態傳遞給一系列的潛在接收者,直至其中一名接收者對請求進行處理。
    • 命令在傳送者和請求者之間建立單向連線。
    • 中介者清除了傳送者和請求者之間的直接連線,強制它們通過一箇中介物件進行間接溝通。
    • 觀察者允許接收者動態地訂閱或取消接收請求。
  • 外觀模式中介者模式的職責類似:它們都嘗試在大量緊密耦合的類中組織起合作。
    • 外觀為子系統中的所有物件定義了一個簡單介面,但是它不提供任何新功能。子系統本身不會意識到外觀的存在。子系統中的物件可以直接進行交流。
    • 中介者將系統中元件的溝通行為中心化。各元件只知道中介者物件,無法直接相互交流。
  • 中介者模式觀察者模式之間往往很難區分。在大部分情況下,可以使用其中一種模式,而有時可以同時使用。
    • 中介者的主要目標是消除一系列系統元件之間的相互依賴。這些元件將依賴於同一個中介者物件。觀察者的目標是在物件之間建立動態的單向連線,使得部分物件可作為其他物件的附屬發揮作用。
    • 有一種流行的中介者模式實現方式依賴於觀察者。中介者物件擔當釋出者的角色,其他元件則作為訂閱者,可以訂閱中介者的事件或取消訂閱。當中介者以這種方式實現時,它可能看上去與觀察者非常相似。
    • 假設有一個程式,其所有的元件都變成了釋出者,它們之間可以相互建立動態連線。這樣程式中就沒有中心化的中介者物件,而只有一些分散式的觀察者。