1 中介者模式的基本概念
C++中的中介者模式(Mediator Pattern)是一种行为型设计模式,主要用于降低多个对象和类之间的通信复杂性。这种模式通过引入一个中介类来封装和处理不同类之间的通信,从而实现松耦合,使代码更易于维护。
在中介者模式中,各个对象不需要显式地相互引用,而是通过中介者进行通信。这样,对象之间的依赖关系被简化,降低了类的复杂度,并将一对多的依赖关系转化成了一对一的依赖关系。同时,这也符合迪米特原则,即一个对象应当对其他对象有最少的了解。
然而,需要注意的是,如果中介者变得过于庞大和复杂,可能会导致其本身的维护变得困难。因此,在职责混乱的情况下,不应使用中介者模式。
在中介者模式中,主要涉及到以下几个角色:
(1)中介者(Mediator):
- 中介者类是整个模式的核心,它负责协调各个同事对象之间的交互,降低了同事之间的耦合度。
- 中介者类通常知道所有的同事对象,并可以在同事对象之间传递消息。
- 中介者可以独立地改变它们之间的交互方式,使得对象之间的交互更加灵活。
(2)同事类(Colleague):
- 同事类(也称为关联类)是那些需要相互通信的对象。
- 同事类不需要直接与其他同事类通信,而是通过中介者来进行交互。
- 同事类通常知道中介者的存在,并可以通过中介者发送和接收消息。
(3)抽象中介者(AbstractMediator):
- 这是一个中介者的抽象父类,定义了中介者的通用接口和行为。
- 具体的中介者类会继承自这个抽象类,并实现具体的交互逻辑。
(4)具体中介者(ConcreteMediator):
- 具体中介者类是中介者的具体实现,它包含了与同事类交互的具体逻辑。
- 具体中介者知道所有的同事对象,并可以协调它们之间的交互。
(5)抽象同事类(AbstractColleague):
- 这是一个同事类的抽象父类,定义了同事类的通用接口和行为。
- 具体的同事类会继承自这个抽象类,并实现具体的交互逻辑。
(6)具体同事类(ConcreteColleague):
- 具体同事类是同事类的具体实现,它包含了与中介者和其他同事类交互的具体逻辑。
- 具体同事类通过中介者来发送和接收消息,实现与其他同事类的通信。
2 中介者模式的实现步骤
中介者模式的实现步骤如下:
(1)定义抽象中介者类:
创建一个抽象中介者类,定义用于同事类之间通信的方法。这些方法可能包括注册同事、接收来自同事的消息、并转发给相应的同事等。
(2)定义具体中介者类:
继承自抽象中介者类,并实现其中的方法。具体中介者类将负责存储和管理同事类对象,协调它们之间的交互。
(3)定义抽象同事类:
创建一个抽象同事类,定义一个接口用于与中介者进行通信。同事类将通过中介者来发送和接收消息,而不是直接与其他同事类交互。
(4)定义具体同事类:
继承自抽象同事类,并实现其中的方法。具体同事类将包含与中介者进行通信的代码,例如发送消息或接收来自中介者的通知。
(5)在同事类中使用中介者:
在具体同事类的实现中,创建对中介者的引用或指针,并通过中介者发送和接收消息。同事类不再直接与其他同事类通信,而是将消息传递给中介者,由中介者负责转发。
(6)初始化中介者和同事类:
在程序初始化时,创建具体中介者对象和具体的同事类对象。将同事类对象注册到中介者中,以便中介者能够管理和协调它们之间的交互。
(7)实现中介者的协调逻辑:
在具体中介者类中,实现协调同事类交互的逻辑。当接收到来自同事类的消息时,中介者根据消息类型和接收者信息,将消息转发给相应的同事类。
如下为样例代码:
cpp
#include <iostream>
#include <memory>
#include <vector>
#include <string>
class AbstractColleague;
// 抽象中介者类
class AbstractMediator {
public:
virtual ~AbstractMediator() = default;
virtual void registerColleague(const std::shared_ptr<AbstractColleague>& colleague) = 0;
virtual void colleagueChanged(const std::shared_ptr<AbstractColleague>& colleague) = 0;
};
// 抽象同事类
class AbstractColleague : public std::enable_shared_from_this<AbstractColleague> {
public:
AbstractColleague(const std::shared_ptr<AbstractMediator>& mediator) : mediator(mediator) {}
virtual ~AbstractColleague() = default;
virtual void send(const std::string& message) = 0;
virtual void receive(const std::string& message) = 0;
void setMediator(const std::shared_ptr<AbstractMediator>& mediator) { this->mediator = mediator; }
protected:
std::weak_ptr<AbstractMediator> mediator;
};
// 具体中介者类
class ConcreteMediator : public AbstractMediator {
public:
void registerColleague(const std::shared_ptr<AbstractColleague>& colleague) override {
colleagues.push_back(colleague);
}
void colleagueChanged(const std::shared_ptr<AbstractColleague>& colleague) override {
for (const auto& c : colleagues) {
if (c != colleague) {
c->receive("Someone has changed");
}
}
}
private:
std::vector<std::shared_ptr<AbstractColleague>> colleagues;
};
// 具体同事类
class ConcreteColleague : public AbstractColleague {
public:
ConcreteColleague(const std::string& name, const std::shared_ptr<AbstractMediator>& mediator)
: AbstractColleague(mediator), name(name) {}
void send(const std::string& message) override {
std::cout << name << " sends: " << message << std::endl;
if (auto med = mediator.lock()) {
med->colleagueChanged(shared_from_this());
}
}
void receive(const std::string& message) override {
std::cout << name << " receives: " << message << std::endl;
}
private:
std::string name;
};
int main()
{
// 创建中介者对象
auto mediator = std::make_shared<ConcreteMediator>();
// 创建同事类对象,并将中介者注入
auto colleagueA = std::make_shared<ConcreteColleague>("ColleagueA", mediator);
auto colleagueB = std::make_shared<ConcreteColleague>("ColleagueB", mediator);
// 注册同事类到中介者
mediator->registerColleague(colleagueA);
mediator->registerColleague(colleagueB);
// 同事类通过中介者进行通信
colleagueA->send("Hello from A");
return 0;
}
上面代码的输出为:
ColleagueA sends: Hello from A
ColleagueB receives: Someone has changed
这个例子定义了一个 AbstractMediator 抽象中介者类和一个 ConcreteMediator 具体中介者类。同样地,例子中也定义了 AbstractColleague 抽象同事类和一个 ConcreteColleague 具体同事类。
ConcreteColleague 类中的 send 方法发送消息,并通过中介者通知其他同事类有变化发生。receive 方法用于接收来自中介者的消息。
在 main 函数中,创建了一个 ConcreteMediator 对象和两个 ConcreteColleague 对象,并将中介者的智能指针传递给同事类。然后,注册同事类到中介者,并通过 send 方法让同事类A发送消息。中介者接收到消息后,会通知所有其他同事类有变化发生。
3 中介者模式的应用场景
C++ 中介者模式的应用场景广泛,主要适用于以下情况:
(1)事件驱动的系统: 在事件驱动的系统中,中介者模式可以用于管理事件的分发和处理。中介者可以接收来自不同对象的事件,并根据事件的类型和接收者信息,将事件转发给相应的对象进行处理。
(2)对象间交互复杂: 当多个对象之间存在错综复杂的交互关系,并且这些交互关系导致代码难以理解和维护时,可以使用中介者模式来简化这些交互。通过引入一个中介者对象,可以将对象之间的直接交互转化为通过中介者进行间接交互,从而降低对象之间的耦合度。
(3)系统需要灵活配置: 当系统中的对象交互方式需要经常变更时,使用中介者模式可以使得这些变更更加容易实现。中介者模式允许独立地改变对象之间的交互方式,而不需要修改大量的代码。
具体示例包括但不限于聊天程序、GUI 编程、网络编程等场景。在聊天程序中,多个用户对象和一个聊天室对象可以通过中介者模式进行通信,用户对象之间不直接通信,而是通过聊天室对象进行消息的发送和接收。这样可以有效地解耦用户对象和聊天室对象,提高代码的可维护性和可扩展性。
3.1 中介者模式应用于事件驱动的系统
在事件驱动的系统中,中介者模式非常适合用于管理事件的分发和处理。下面是一个中介者模式应用于事件驱动系统的样例,这个样例模拟了一个简单的 GUI 系统,其中按钮(Button)和文本框(TextBox)通过事件中介者(EventManager)来交互。
首先,定义事件和事件监听器的接口:
cpp
#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include <string>
#include <algorithm>
// 事件类型
enum class EventType {
BUTTON_CLICKED,
TEXT_CHANGED
};
// 事件基类
class Event {
public:
virtual EventType getType() const = 0;
virtual ~Event() = default;
};
// 点击事件
class ButtonClickedEvent : public Event {
public:
ButtonClickedEvent(const std::string& buttonName) : buttonName(buttonName) {}
EventType getType() const override { return EventType::BUTTON_CLICKED; }
const std::string& getButtonName() const { return buttonName; }
private:
std::string buttonName;
};
// 文本改变事件
class TextChangedEvent : public Event {
public:
TextChangedEvent(const std::string& textBoxName, const std::string& newText)
: textBoxName(textBoxName), newText(newText) {}
EventType getType() const override { return EventType::TEXT_CHANGED; }
const std::string& getTextBoxName() const { return textBoxName; }
const std::string& getNewText() const { return newText; }
private:
std::string textBoxName;
std::string newText;
};
// 事件监听器接口
class EventListener {
public:
virtual void handleEvent(const std::shared_ptr<Event>& event) = 0;
virtual ~EventListener() = default;
};
接着,定义事件中介者(EventManager)和可以注册监听器并触发事件的组件(如 Button 和 TextBox):
cpp
// 事件中介者
class EventManager {
public:
using EventListenerPtr = std::shared_ptr<EventListener>;
using EventPtr = std::shared_ptr<Event>;
void registerListener(const std::string& eventName, const EventListenerPtr& listener) {
listeners[eventName].push_back(listener);
}
void unregisterListener(const std::string& eventName, const EventListenerPtr& listener) {
auto it = listeners.find(eventName);
if (it != listeners.end()) {
auto it2 = std::remove_if(it->second.begin(), it->second.end(), [&](const EventListenerPtr& listener_) {
return listener_ == listener;
});
it->second.erase(it2, it->second.end());
}
}
void triggerEvent(const EventPtr& event) {
std::string eventName = getEventName(event->getType());
for (const auto& listener : listeners[eventName]) {
listener->handleEvent(event);
}
}
private:
static std::string getEventName(EventType type) {
switch (type) {
case EventType::BUTTON_CLICKED: return "BUTTON_CLICKED";
case EventType::TEXT_CHANGED: return "TEXT_CHANGED";
default: return "UNKNOWN_EVENT";
}
}
std::unordered_map<std::string, std::vector<EventListenerPtr>> listeners;
};
// 按钮类
class Button {
public:
Button(const std::string& name, const EventManager::EventListenerPtr& listener)
: name(name), clickedListener(listener) {}
void onClick() {
auto event = std::make_shared<ButtonClickedEvent>(name);
clickedListener->handleEvent(event);
}
private:
std::string name;
EventManager::EventListenerPtr clickedListener;
};
// 文本框类
class TextBox {
public:
TextBox(const std::string& name, const EventManager::EventListenerPtr& listener)
: name(name), textChangedListener(listener) {}
void setText(const std::string& newText) {
auto event = std::make_shared<TextChangedEvent>(name, newText);
textChangedListener->handleEvent(event);
}
private:
std::string name;
EventManager::EventListenerPtr textChangedListener;
};
最后,创建具体的监听器实现,并展示如何使用这些组件和中介者:
cpp
// 具体的监听器实现
class TextBoxListener : public EventListener {
public:
void handleEvent(const std::shared_ptr<Event>& event) override {
if (event->getType() == EventType::BUTTON_CLICKED) {
auto clickedEvent = std::static_pointer_cast<ButtonClickedEvent>(event);
std::cout << "TextBoxListener: Button " << clickedEvent->getButtonName()
<< " was clicked!" << std::endl;
// 响应按钮点击事件,比如更新文本框内容等
}
}
};
int main()
{
// 创建事件中介者
EventManager eventManager;
// 创建监听器
auto textBoxListener = std::make_shared<TextBoxListener>();
// 创建按钮并注册监听器
Button button("MyButton", textBoxListener);
// 注册事件到中介者
eventManager.registerListener("BUTTON_CLICKED", textBoxListener);
// 模拟按钮点击事件
button.onClick();
return 0;
}
上面代码的输出为:
TextBoxListener: Button MyButton was clicked!
3.2 中介者模式应用于对象间交互复杂
以一个聊天室应用为例,其中多个用户(对象)通过聊天室(中介者)进行交互。以下是一个简化版的C++样例,展示了如何使用中介者模式来处理用户之间的消息传递。
首先,定义用户(User)类,它能够发送消息:
cpp
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class AbstractChatRoom
{
public:
virtual void sendMessage(const std::string& sender, const std::string& message) = 0;
};
class User {
public:
User(const std::string& name, std::shared_ptr<AbstractChatRoom> chatRoom)
: m_name(name), m_chatRoom(chatRoom) {}
void sendMessage(const std::string& message) {
m_chatRoom->sendMessage(m_name, message);
}
void receiveMessage(const std::string& sender, const std::string& message) {
std::cout << sender << ": " << message << std::endl;
}
std::string getName() {
return m_name;
}
private:
std::string m_name;
std::shared_ptr<AbstractChatRoom> m_chatRoom;
};
接下来,定义聊天室(ChatRoom)类,作为中介者:
cpp
class ChatRoom : public AbstractChatRoom {
public:
void registerUser(const std::shared_ptr<User>& user) {
m_users.push_back(user);
}
void sendMessage(const std::string& sender, const std::string& message) override {
for (const auto& user : m_users) {
if (user->getName() != sender) {
user->receiveMessage(sender, message);
}
}
}
private:
std::vector<std::shared_ptr<User>> m_users;
};
现在,可以创建一个主程序来模拟多个用户通过聊天室进行交互:
cpp
int main()
{
// 创建聊天室中介者
std::shared_ptr<ChatRoom> chatRoom = std::make_shared<ChatRoom>();
// 创建用户并注册到聊天室
std::shared_ptr<User> user1 = std::make_shared<User>("Alice", chatRoom);
std::shared_ptr<User> user2 = std::make_shared<User>("Bob", chatRoom);
std::shared_ptr<User> user3 = std::make_shared<User>("Charlie", chatRoom);
chatRoom->registerUser(user1);
chatRoom->registerUser(user2);
chatRoom->registerUser(user3);
// 用户发送消息
user1->sendMessage("Hello, everyone!");
user2->sendMessage("Hi, Alice. What's up?");
user3->sendMessage("I'm good, thanks. Nice to meet you all.");
return 0;
}
上面代码的输出为:
Alice: Hello, everyone!
Alice: Hello, everyone!
Bob: Hi, Alice. What's up?
Bob: Hi, Alice. What's up?
Charlie: I'm good, thanks. Nice to meet you all.
Charlie: I'm good, thanks. Nice to meet you all.
这个样例中创建了三个用户(Alice、Bob 和 Charlie),并将它们注册到聊天室中。每个用户都可以发送消息,消息通过聊天室中介者传递给其他所有用户。这样,用户之间不需要直接交互,而是通过中介者进行通信,从而简化了对象间的交互。
4 中介者模式的优点与缺点
C++ 中介者模式的优点主要包括:
(1)降低对象间的耦合度: 通过中介者对象进行通信,减少了对象之间的直接依赖,降低了系统的耦合度。这使得对象可以更加独立地改变和复用,提高了系统的灵活性和可维护性。
(2)简化对象间的交互: 中介者模式将多个对象之间的复杂交互关系转换为中介者与对象之间的一对多关系,使得对象之间的交互变得简单和清晰。这有助于降低代码的复杂性,提高代码的可读性和可维护性。
(3)集中控制交互: 通过中介者对象,可以对对象间的交互进行集中控制和管理。这使得系统更容易进行协调、监控和扩展。
(4)易于添加新对象: 当系统中需要添加新的对象时,只需要将该对象与中介者对象建立联系,而不需要修改其他对象。这降低了添加新对象的难度和成本。
然而,C++ 中介者模式也存在一些缺点:
(1)可能导致中介者对象过于复杂: 如果系统中对象间的交互关系非常复杂,中介者对象可能会变得庞大和复杂,难以管理和维护。这可能会抵消掉降低对象间耦合度带来的优势。
(2)可能产生过多的中介者: 如果系统中有多个不同类型的交互关系,可能需要创建多个中介者对象来分别处理这些关系。这会增加系统的复杂性和管理成本。
(3)对中介者的依赖: 系统中的对象都需要依赖于中介者对象进行交互,这增加了对中介者的依赖。如果中介者对象出现问题或失效,整个系统可能会受到影响。
(4)不适用于所有场景: 虽然中介者模式在某些场景下非常有用,但并不是所有场景都适合使用中介者模式。在一些简单的系统中,对象间的交互关系并不复杂,使用中介者模式可能会引入不必要的复杂性和开销。