文章

中介者模式 (Mediator Pattern) 深度解析

中介者模式 (Mediator Pattern) 深度解析

🧑‍🤝‍🧑 中介者模式 (Mediator Pattern) 深度解析

1. 模式动机:解决“蜘蛛网”式的复杂交互

在用户直接聊天的设计方案中,用户对象之间存在很强的关联性,将导致系统出现“蜘蛛网”式的复杂耦合结构。

  • 系统结构复杂: 对象间存在大量的相互关联和直接调用。若有一个对象发生变化,需要跟踪所有关联对象进行处理。
  • 对象可重用性差: 由于强关联,一个对象很难被另一个系统或模块重用,表现得像一个不可分割的整体。
  • 系统扩展性低: 增加新的对象需要调整原有相关对象上的引用,系统耦合度很高,扩展性差。

根据“单一职责原则”,对象应尽量细化。中介者模式正是为了解决这种对象两两之间复杂的引用关系,使系统成为一个松耦合 的星形结构。

2. 模式定义

中介者模式 (Mediator Pattern) 定义:

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以* *独立地改变它们之间的交互**。

中介者模式又称为调停者模式,它是一种对象行为型模式。

3. 模式结构与角色

角色名称职责描述
Mediator (抽象中介者)定义一个接口,用于各同事对象之间的通信。
ConcreteMediator (具体中介者)实现抽象接口,通过协调和封装同事对象的交互来实现协作行为。它了解并维护所有同事对象的引用。
Colleague (抽象同事类)定义同事对象的公有方法,并通常会引用一个中介者对象。
ConcreteColleague (具体同事类)抽象同事类的子类,是需要通过中介者进行通信的具体对象。它通过中介者间接完成与其他同事类的通信。

4. 中介者的职责与原理分析

中介者承担了两个核心职责:

  1. 中转作用(结构性): 各同事对象只需通过中介者即可通信,避免了显式引用其他同事。
  2. 协调作用(行为性): 中介者封装了同事之间的协调逻辑,对同事的请求进行进一步处理。

通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构

时序图

5. 代码示例(多语言)

5.1. C++ 示例 (聊天消息转发)

此示例展示了 ConcreteColleagueAConcreteColleagueB 如何通过 ConcreteMediator 进行消息转发,从而实现解耦。

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include "ConcreteColleagueA.h"
#include "ConcreteMediator.h"
#include "ConcreteColleagueB.h"

using namespace std;

int main(int argc, char *argv[])
{
    ConcreteColleagueA * pa = new ConcreteColleagueA();
    ConcreteColleagueB * pb = new ConcreteColleagueB();
    ConcreteMediator * pm = new ConcreteMediator();
    
    // 注册同事对象
    pm->registered(1,pa);
    pm->registered(2,pb);

    // sendmsg from a to b
    pa->sendmsg(2,"hello,i am a");
    // sendmsg from b to a
    pb->sendmsg(1,"hello,i am b");

    delete pa;
    delete pb;
    delete pm; // 注意:实际应用中需要确保map中的Colleague对象也被正确管理和释放
    return 0;
}

ConcreteMediator.cpp (核心实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 引入必要的头文件...
#include "ConcreteMediator.h"
#include <map>
#include <iostream>
using namespace std;

// ... 构造函数和析构函数省略 ...

/**
 * @brief 中介者的核心操作:根据 nWho 查找同事并转发消息
 * @param nWho 目标同事的ID
 * @param str 要发送的消息
 */
void ConcreteMediator::operation(int nWho,string str){
    map<int,Colleague*>::const_iterator itr = m_mpColleague.find(nWho);
    if(itr == m_mpColleague.end())
    {
        cout << "not found this colleague!" << endl;
        return;
    }

    Colleague* pc = itr->second;
    pc->receivemsg(str); // 转发消息给目标同事
}


void ConcreteMediator::registered(int nWho,Colleague * aColleague){
    m_mpColleague.insert(make_pair(nWho,aColleague));
    // 同时将中介类暴露给colleague,建立双向引用(Colleague需知晓Mediator才能发送消息)
    aColleague->setMediator(this);
}

ConcreteColleagueA.cpp (发送方实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 引入必要的头文件...
#include "ConcreteColleagueA.h"
#include <iostream>
using namespace std;

// ... 构造函数和析构函数省略 ...

/**
 * @brief 发送消息,不直接引用其他同事,而是委托给中介者
 */
void ConcreteColleagueA::sendmsg(int toWho,string str){
    cout << "send msg from colleagueA,to:" << toWho << endl;
    // 核心:通过中介者进行操作
    m_pMediator->operation(toWho,str); 
}

void ConcreteColleagueA::receivemsg(string str){
    cout << "ConcreteColleagueA reveivemsg:" << str <<endl;
}

5.2. Java 示例 (聊天室)

此示例使用 Java 语言实现一个虚拟聊天室,ChatRoom 作为具体中介者。

ChatRoom.java (ConcreteMediator)

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Date;

public class ChatRoom {
    /**
     * 静态方法模拟消息转发和协调行为
     */
    public static void showMessage(User user, String message) {
        // 协调行为:增加时间戳、过滤等逻辑可以在此处实现
        String filteredMessage = message.replace("日", "*"); // 示例:过滤不雅字符
        System.out.println(new Date().toString()
                + " [" + user.getName() + "] : " + filteredMessage);
    }
}

User.java (ConcreteColleague)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void sendMessage(String message) {
        // 核心:将消息发送(委托)给中介者
        ChatRoom.showMessage(this, message);
    }
}

MediatorPatternDemo.java

1
2
3
4
5
6
7
8
9
10
public class MediatorPatternDemo {
    public static void main(String[] args) {
        User robert = new User("Robert");
        User john = new User("John");

        robert.sendMessage("Hi! John!");
        john.sendMessage("Hello! Robert!");
        robert.sendMessage("今天天气真好日"); // 包含被过滤的字符
    }
}

5.3. Python 示例

此示例使用 Python 语言实现,结构清晰地展示了中介者和同事类的关系。

mediator_pattern.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Mediator:
    """抽象中介者"""

    def notify(self, sender, event):
        pass


class ConcreteMediator(Mediator):
    """具体中介者:协调同事之间的交互"""

    def __init__(self, c1, c2):
        self.colleague1 = c1
        self.colleague1.mediator = self
        self.colleague2 = c2
        self.colleague2.mediator = self

    def notify(self, sender, event):
        if sender == self.colleague1 and event == "A":
            print("Mediator reacts to A and triggers B's action.")
            self.colleague2.do_b()
        elif sender == self.colleague2 and event == "D":
            print("Mediator reacts to D and triggers A's action.")
            self.colleague1.do_c()


class Colleague:
    """抽象同事类"""

    def __init__(self, mediator=None):
        self.mediator = mediator


class ConcreteColleague1(Colleague):
    """具体同事类 1"""

    def do_a(self):
        print("Colleague 1 does A.")
        # 委托给中介者
        self.mediator.notify(self, "A")

    def do_c(self):
        print("Colleague 1 does C.")


class ConcreteColleague2(Colleague):
    """具体同事类 2"""

    def do_b(self):
        print("Colleague 2 does B.")

    def do_d(self):
        print("Colleague 2 does D.")
        # 委托给中介者
        self.mediator.notify(self, "D")


# 客户端代码
c1 = ConcreteColleague1()
c2 = ConcreteColleague2()
mediator = ConcreteMediator(c1, c2)

print("Client triggers Colleague 1's action:")
c1.do_a()

print("\nClient triggers Colleague 2's action:")
c2.do_d()

6. 模式应用、优缺点与总结

6.1. 模式应用

  • MVC 架构: Controller(控制器)作为 Model(模型)和 View(视图)之间的中介者。
  • 机场调度系统: 塔台充当各飞机之间的中介者。
  • GUI 开发: 在复杂的界面中,中介者类负责协调多个界面组件(同事类)之间的交互关系。

6.2. 优缺点

优点 (Benefits)缺点 (Drawbacks)
简化交互与解耦: 将复杂的网状引用关系转化为星形结构,各同事对象彼此解耦,只依赖于中介者。中介者复杂化: 所有的交互逻辑都集中在具体中介者类中,可能导致它非常庞大和复杂,难以维护。
符合迪米特法则: 减少了对象之间直接引用的数目。 
提高复用性/扩展性: 改变交互行为只需修改或增加中介者类。 

6.3. 适用环境

  • 系统中对象之间存在复杂的引用关系,相互依赖关系结构混乱且难以理解。
  • 一个对象直接引用并与其他很多对象通信,导致难以复用该对象。
  • 想通过一个中间类来封装多个类中的行为,从而集中管理和控制这些交互。

总结: 中介者模式通过引入一个中介者对象,成功地将同事对象之间的网状耦合关系解开,转化为以中介者为中心的松耦合星形结构,简化了对象间的交互,是迪米特法则的典型应用。

本文由作者按照 CC BY 4.0 进行授权