实用指南:中介者模式:解耦对象交互的艺术
行为型设计模式之中介者模式
文章目录
1. 中介者模式简介
中介者模式(Mediator Pattern)是一种行为型设计模式,它用一个中介对象来封装一系列对象之间的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
在软件系统中,随着类的不断增加,类与类之间的依赖关系会变得非常复杂,形成一个网状结构,这种情况被称为"类爆炸"。中介者模式的主要目的就是将这种网状依赖结构转变为星型结构,从而降低系统的复杂度。
中介者模式的核心思想是:将相关对象之间的通信委托给中介者对象处理,对象之间不再直接通信,而是通过中介者来协调它们的交互。这样,当一个对象发生变化时,只需要通知中介者,由中介者通知相关的其他对象做出响应。
2. 中介者模式的结构
中介者模式主要包含以下几个角色:
- Mediator(中介者):定义了一个接口,用于各同事(Colleague)对象之间的通信。
- ConcreteMediator(具体中介者):实现中介者接口,协调各同事对象之间的交互关系。了解并维护着各个同事对象。
- Colleague(同事):定义一个抽象类,维护一个指向中介者的引用,每个同事都知道它的中介者对象。
- ConcreteColleague(具体同事):实现抽象同事类,通过中介者与其他同事通信。
3. 中介者模式的实现(C#)
下面通过几个不同的C#实现来展示中介者模式。
3.1 基本实现
首先,我们创建一个简单的聊天室系统,展示中介者模式的基本实现:
using System;
using System.Collections.Generic;
// 中介者接口
public interface IChatMediator
{
void SendMessage(string msg, User user); // 发送消息
void AddUser(User user); // 添加用户
}
// 抽象用户类
public abstract class User
{
protected IChatMediator mediator; // 对中介者的引用
protected string name; // 用户名
// 构造函数
public User(IChatMediator mediator, string name)
{
this.mediator = mediator;
this.name = name;
}
// 发送消息
public abstract void Send(string msg);
// 接收消息
public abstract void Receive(string msg);
}
// 具体中介者:聊天室
public class ChatRoom : IChatMediator
{
private List<User> users; // 用户列表
// 构造函数
public ChatRoom()
{
users = new List<User>();
}
// 添加用户到聊天室
public void AddUser(User user)
{
users.Add(user);
}
// 将消息发送给所有用户,除了发送者
public void SendMessage(string msg, User user)
{
foreach (var u in users)
{
// 不发送给自己
if (u != user)
{
u.Receive(msg);
}
}
}
}
// 具体用户:普通用户
public class NormalUser : User
{
// 构造函数
public NormalUser(IChatMediator mediator, string name) : base(mediator, name)
{
}
// 发送消息
public override void Send(string msg)
{
Console.WriteLine($"{name} 发送消息: {msg}");
mediator.SendMessage(msg, this);
}
// 接收消息
public override void Receive(string msg)
{
Console.WriteLine($"{name} 收到消息: {msg}");
}
}
// 具体用户:管理员用户
public class AdminUser : User
{
// 构造函数
public AdminUser(IChatMediator mediator, string name) : base(mediator, name)
{
}
// 发送消息(带有管理员标记)
public override void Send(string msg)
{
Console.WriteLine($"管理员 {name} 发送消息: {msg}");
mediator.SendMessage($"[管理员消息] {msg}", this);
}
// 接收消息
public override void Receive(string msg)
{
Console.WriteLine($"管理员 {name} 收到消息: {msg}");
}
}
// 客户端代码
public class Program
{
static void Main(string[] args)
{
// 创建中介者:聊天室
IChatMediator chatroom = new ChatRoom();
// 创建用户
User user1 = new NormalUser(chatroom, "张三");
User user2 = new NormalUser(chatroom, "李四");
User user3 = new NormalUser(chatroom, "王五");
User admin = new AdminUser(chatroom, "管理员");
// 将用户添加到聊天室
chatroom.AddUser(user1);
chatroom.AddUser(user2);
chatroom.AddUser(user3);
chatroom.AddUser(admin);
// 用户发送消息
user1.Send("大家好!");
Console.WriteLine();
admin.Send("请注意聊天室规则。");
Console.WriteLine();
user2.Send("收到,谢谢提醒!");
}
}
3.2 使用事件和委托的实现
在C#中,我们可以利用事件和委托机制来实现中介者模式,使代码更加符合C#的风格:
using System;
using System.Collections.Generic;
// 消息事件参数
public class MessageEventArgs : EventArgs
{
public string Message { get; } // 消息内容
public string SenderName { get; } // 发送者名称
public MessageEventArgs(string message, string senderName)
{
Message = message;
SenderName = senderName;
}
}
// 中介者:聊天服务
public class ChatService
{
// 消息发送事件
public event EventHandler<MessageEventArgs> MessageSent;
// 用户字典,键为用户名
private Dictionary<string, IChatClient> clients = new Dictionary<string, IChatClient>();
// 注册客户端
public void RegisterClient(IChatClient client)
{
if (!clients.ContainsKey(client.Name))
{
clients.Add(client.Name, client);
// 订阅客户端的发送消息事件
client.SendMessage += OnClientSendMessage;
Console.WriteLine($"客户端 {client.Name} 已注册");
}
}
// 注销客户端
public void UnregisterClient(IChatClient client)
{
if (clients.ContainsKey(client.Name))
{
clients.Remove(client.Name);
// 取消订阅客户端的发送消息事件
client.SendMessage -= OnClientSendMessage;
Console.WriteLine($"客户端 {client.Name} 已注销");
}
}
// 处理客户端发送消息事件
private void OnClientSendMessage(object sender, MessageEventArgs e)
{
// 触发消息发送事件,通知所有客户端
MessageSent?.Invoke(this, e);
}
// 发送私信
public void SendPrivateMessage(string message, string senderName, string receiverName)
{
if (clients.ContainsKey(receiverName))
{
// 直接向特定客户端发送消息
clients[receiverName].ReceiveMessage(message, senderName, true);
}
}
}
// 聊天客户端接口
public interface IChatClient
{
string Name { get; } // 客户端名称
event EventHandler<MessageEventArgs> SendMessage; // 发送消息事件
void ReceiveMessage(string message, string senderName, bool isPrivate = false); // 接收消息
}
// 具体聊天客户端
public class ChatClient : IChatClient
{
private ChatService chatService; // 聊天服务(中介者)
public string Name { get; private set; } // 客户端名称
// 发送消息事件
public event EventHandler<MessageEventArgs> SendMessage;
// 构造函数
public ChatClient(string name, ChatService chatService)
{
Name = name;
this.chatService = chatService;
// 订阅中介者的消息发送事件
chatService.MessageSent += OnMessageReceived;
// 注册到中介者
chatService.RegisterClient(this);
}
// 处理收到的消息
private void OnMessageReceived(object sender, MessageEventArgs e)
{
// 不接收自己发送的消息
if (e.SenderName != Name)
{
Console.WriteLine($"{Name} 收到来自 {e.SenderName} 的消息: {e.Message}");
}
}
// 向所有客户端发送消息
public void BroadcastMessage(string message)
{
Console.WriteLine($"{Name} 发送广播消息: {message}");
// 触发发送消息事件,通知中介者
SendMessage?.Invoke(this, new MessageEventArgs(message, Name));
}
// 发送私信
public void SendPrivateMessage(string message, string receiverName)
{
Console.WriteLine($"{Name} 向 {receiverName} 发送私信: {message}");
chatService.SendPrivateMessage(message, Name, receiverName);
}
// 接收消息
public void ReceiveMessage(string message, string senderName, bool isPrivate = false)
{
string messageType = isPrivate ? "私信" : "消息";
Console.WriteLine($"{Name} 收到来自 {senderName} 的{messageType}: {message}");
}
// 析构函数,注销客户端
~ChatClient()
{
chatService.MessageSent -= OnMessageReceived;
chatService.UnregisterClient(this);
}
}
// 客户端代码
public class Program
{
static void Main(string[] args)
{
// 创建中介者:聊天服务
ChatService chatService = new ChatService();
// 创建聊天客户端
ChatClient client1 = new ChatClient("张三", chatService);
ChatClient client2 = new ChatClient("李四", chatService);
ChatClient client3 = new ChatClient("王五", chatService);
// 客户端发送广播消息
client1.BroadcastMessage("大家好!");
Console.WriteLine();
// 客户端发送私信
client2.SendPrivateMessage("你好,有时间聊聊吗?", "张三");
Console.WriteLine();
client3.BroadcastMessage("今天天气真好!");
}
}
3.3 GUI控件交互实现
以下是一个模拟GUI控件交互的中介者模式实现,展示了如何使用中介者模式来协调表单控件之间的交互:
using System;
using System.Collections.Generic;
// 抽象中介者
public interface IDialogMediator
{
void ControlChanged(Control control); // 当控件状态改变时调用
}
// 抽象控件类
public abstract class Control
{
protected IDialogMediator mediator; // 对中介者的引用
protected string name; // 控件名称
// 构造函数
public Control(IDialogMediator mediator, string name)
{
this.mediator = mediator;
this.name = name;
}
// 控件名称属性
public string Name => name;
// 当控件状态改变时,通知中介者
public void Changed()
{
mediator.ControlChanged(this);
}
// 具体控件需要实现的事件处理方法
public abstract void HandleEvent();
}
// 具体控件:按钮
public class Button : Control
{
private bool enabled; // 按钮是否启用
// 构造函数
public Button(IDialogMediator mediator, string name) : base(mediator, name)
{
enabled = true;
}
// 启用/禁用按钮
public void SetEnabled(bool enabled)
{
this.enabled = enabled;
Console.WriteLine($"按钮 '{name}' {(enabled ? "已启用" : "已禁用")}");
}
// 点击按钮
public void Click()
{
if (enabled)
{
Console.WriteLine($"按钮 '{name}' 被点击");
Changed(); // 通知中介者
}
else
{
Console.WriteLine($"按钮 '{name}' 已禁用,无法点击");
}
}
// 处理事件
public override void HandleEvent()
{
Console.WriteLine($"按钮 '{name}' 处理事件");
}
}
// 具体控件:文本框
public class TextBox : Control
{
private string text; // 文本内容
// 构造函数
public TextBox(IDialogMediator mediator, string name) : base(mediator, name)
{
text = "";
}
// 获取文本内容
public string GetText()
{
return text;
}
// 设置文本内容
public void SetText(string text)
{
this.text = text;
Console.WriteLine($"文本框 '{name}' 的内容设置为: {text}");
Changed(); // 通知中介者
}
// 处理事件
public override void HandleEvent()
{
Console.WriteLine($"文本框 '{name}' 处理事件");
}
}
// 具体控件:复选框
public class CheckBox : Control
{
private bool checked_; // 是否选中
// 构造函数
public CheckBox(IDialogMediator mediator, string name) : base(mediator, name)
{
checked_ = false;
}
// 是否选中
public bool IsChecked()
{
return checked_;
}
// 设置选中状态
public void SetChecked(bool checked_)
{
this.checked_ = checked_;
Console.WriteLine($"复选框 '{name}' {(checked_ ? "已选中" : "已取消选中")}");
Changed(); // 通知中介者
}
// 处理事件
public override void HandleEvent()
{
Console.WriteLine($"复选框 '{name}' 处理事件");
}
}
// 具体中介者:用户注册对话框
public class UserRegistrationDialog : IDialogMediator
{
// 对话框中的控件
private TextBox usernameTextBox;
private TextBox passwordTextBox;
private Button okButton;
private Button cancelButton;
private CheckBox agreeTermsCheckBox;
// 初始化对话框及其控件
public void InitComponents()
{
usernameTextBox = new TextBox(this, "用户名");
passwordTextBox = new TextBox(this, "密码");
okButton = new Button(this, "确定");
cancelButton = new Button(this, "取消");
agreeTermsCheckBox = new CheckBox(this, "同意条款");
// 初始状态下,确定按钮禁用
okButton.SetEnabled(false);
}
// 获取控件引用
public TextBox GetUsernameTextBox() => usernameTextBox;
public TextBox GetPasswordTextBox() => passwordTextBox;
public Button GetOkButton() => okButton;
public Button GetCancelButton() => cancelButton;
public CheckBox GetAgreeTermsCheckBox() => agreeTermsCheckBox;
// 实现中介者接口方法,当控件状态改变时调用
public void ControlChanged(Control control)
{
// 根据不同控件的状态变化,协调其他控件的行为
if (control == usernameTextBox || control == passwordTextBox || control == agreeTermsCheckBox)
{
// 验证表单是否可以提交
ValidateForm();
}
else if (control == okButton)
{
// 处理确定按钮点击事件
SubmitForm();
}
else if (control == cancelButton)
{
// 处理取消按钮点击事件
CancelForm();
}
}
// 验证表单
private void ValidateForm()
{
// 检查用户名和密码是否已填写,以及是否同意条款
bool isValid = !string.IsNullOrEmpty(usernameTextBox.GetText()) &&
!string.IsNullOrEmpty(passwordTextBox.GetText()) &&
agreeTermsCheckBox.IsChecked();
// 根据验证结果启用或禁用确定按钮
okButton.SetEnabled(isValid);
Console.WriteLine($"表单验证: {(isValid ? "有效" : "无效")}");
}
// 提交表单
private void SubmitForm()
{
Console.WriteLine("表单提交成功!");
Console.WriteLine($"用户名: {usernameTextBox.GetText()}");
Console.WriteLine($"密码: {new string('*', passwordTextBox.GetText().Length)}");
}
// 取消表单
private void CancelForm()
{
Console.WriteLine("表单已取消");
// 清空所有字段
usernameTextBox.SetText("");
passwordTextBox.SetText("");
agreeTermsCheckBox.SetChecked(false);
}
}
// 客户端代码
public class Program
{
static void Main(string[] args)
{
// 创建中介者:用户注册对话框
UserRegistrationDialog dialog = new UserRegistrationDialog();
dialog.InitComponents();
// 获取对话框中的控件
TextBox usernameTextBox = dialog.GetUsernameTextBox();
TextBox passwordTextBox = dialog.GetPasswordTextBox();
CheckBox agreeTermsCheckBox = dialog.GetAgreeTermsCheckBox();
Button okButton = dialog.GetOkButton();
Button cancelButton = dialog.GetCancelButton();
// 模拟用户操作
Console.WriteLine("开始用户注册流程:");
// 设置用户名
usernameTextBox.SetText("john_doe");
// 设置密码
passwordTextBox.SetText("secure123");
// 勾选同意条款(此时确定按钮应该被启用)
agreeTermsCheckBox.SetChecked(true);
// 点击确定按钮(提交表单)
okButton.Click();
Console.WriteLine("\n开始新的注册流程:");
// 点击取消按钮(清空表单)
cancelButton.Click();
// 尝试点击确定按钮(由于表单已清空,按钮应该是禁用的)
okButton.Click();
}
}
4. 中介者模式与观察者模式的区别
中介者模式和观察者模式都是处理对象间通信的设计模式,但它们有一些关键区别:
通信方式:
- 观察者模式:是一对多的关系,主题(Subject)直接通知所有观察者(Observer)。
- 中介者模式:是多对多的关系,各同事(Colleague)对象通过中介者(Mediator)间接通信。
依赖关系:
- 观察者模式:观察者依赖于主题,主题不依赖于观察者。
- 中介者模式:同事对象与中介者相互依赖。
通信流向:
- 观察者模式:通信是单向的,从主题到观察者。
- 中介者模式:通信是双向的,同事对象既可以发送消息也可以接收消息。
目的:
- 观察者模式:主要目的是在状态变化时通知依赖对象。
- 中介者模式:主要目的是减少对象之间的直接依赖关系。
下面是一个对比表,展示两种模式在不同方面的区别:
5. 中介者模式的应用场景
中介者模式适用于以下场景:
复杂的UI交互:如对话框中各控件之间的交互。例如,当用户在一个表单中勾选某个复选框时,其他的输入框可能被启用或禁用。
聊天应用:聊天室中的用户之间不直接通信,而是通过聊天服务器(中介者)来转发消息。
飞行控制系统:机场的塔台控制(中介者)负责协调各飞机(同事)的起飞和降落。
多人游戏:游戏服务器(中介者)协调各玩家(同事)之间的交互。
协同编辑软件:如在线文档编辑工具,其中所有用户的编辑操作都通过中央服务器(中介者)进行协调。
MVC/MVP架构:Controller/Presenter作为中介者,协调Model和View之间的交互。
示例场景:航空交通管制系统
以下是一个航空交通管制系统的简化实现,展示中介者模式在该场景中的应用:
using System;
using System.Collections.Generic;
// 中介者接口:航空交通管制
public interface IAirTrafficControl
{
void RegisterAircraft(Aircraft aircraft); // 注册飞机
void SendMessage(string message, Aircraft sender); // 发送消息
void RequestLanding(Aircraft aircraft); // 请求降落
void RequestTakeoff(Aircraft aircraft); // 请求起飞
}
// 抽象同事类:飞机
public abstract class Aircraft
{
protected IAirTrafficControl atc; // 航空交通管制中心
protected string callSign; // 呼号
protected bool isAirborne; // 是否在空中
// 构造函数
public Aircraft(string callSign, IAirTrafficControl atc)
{
this.callSign = callSign;
this.atc = atc;
this.isAirborne = false;
// 向管制中心注册
atc.RegisterAircraft(this);
}
// 获取呼号
public string GetCallSign()
{
return callSign;
}
// 接收消息
public void ReceiveMessage(string message)
{
Console.WriteLine($"{callSign} 收到消息: {message}");
}
// 发送消息到管制中心
public void SendMessage(string message)
{
Console.WriteLine($"{callSign} 发送消息: {message}");
atc.SendMessage(message, this);
}
// 请求起飞
public void RequestTakeoff()
{
if (!isAirborne)
{
Console.WriteLine($"{callSign} 请求起飞");
atc.RequestTakeoff(this);
}
else
{
Console.WriteLine($"{callSign} 已经在空中,无法起飞");
}
}
// 请求降落
public void RequestLanding()
{
if (isAirborne)
{
Console.WriteLine($"{callSign} 请求降落");
atc.RequestLanding(this);
}
else
{
Console.WriteLine($"{callSign} 已经在地面,无法降落");
}
}
// 起飞
public void Takeoff()
{
isAirborne = true;
Console.WriteLine($"{callSign} 起飞成功");
}
// 降落
public void Land()
{
isAirborne = false;
Console.WriteLine($"{callSign} 降落成功");
}
}
// 具体同事类:客机
public class CommercialAircraft : Aircraft
{
private int passengerCapacity; // 乘客容量
// 构造函数
public CommercialAircraft(string callSign, IAirTrafficControl atc, int passengerCapacity)
: base(callSign, atc)
{
this.passengerCapacity = passengerCapacity;
}
}
// 具体同事类:货机
public class CargoAircraft : Aircraft
{
private double cargoCapacity; // 货物容量(吨)
// 构造函数
public CargoAircraft(string callSign, IAirTrafficControl atc, double cargoCapacity)
: base(callSign, atc)
{
this.cargoCapacity = cargoCapacity;
}
}
// 具体中介者:航空交通管制中心
public class AirTrafficControlCenter : IAirTrafficControl
{
private Dictionary<string, Aircraft> aircrafts; // 注册的飞机
private Queue<Aircraft> landingQueue; // 降落队列
private Queue<Aircraft> takeoffQueue; // 起飞队列
private bool runwayFree; // 跑道是否空闲
// 构造函数
public AirTrafficControlCenter()
{
aircrafts = new Dictionary<string, Aircraft>();
landingQueue = new Queue<Aircraft>();
takeoffQueue = new Queue<Aircraft>();
runwayFree = true;
}
// 注册飞机
public void RegisterAircraft(Aircraft aircraft)
{
string callSign = aircraft.GetCallSign();
if (!aircrafts.ContainsKey(callSign))
{
aircrafts.Add(callSign, aircraft);
Console.WriteLine($"飞机 {callSign} 已注册到管制中心");
}
}
// 广播消息给所有飞机,除了发送者
public void SendMessage(string message, Aircraft sender)
{
foreach (var aircraft in aircrafts.Values)
{
if (aircraft != sender)
{
aircraft.ReceiveMessage(message);
}
}
}
// 请求降落
public void RequestLanding(Aircraft aircraft)
{
landingQueue.Enqueue(aircraft);
Console.WriteLine($"飞机 {aircraft.GetCallSign()} 已加入降落队列,当前队列长度: {landingQueue.Count}");
// 如果跑道空闲,处理下一个降落请求
if (runwayFree)
{
ProcessNextOperation();
}
}
// 请求起飞
public void RequestTakeoff(Aircraft aircraft)
{
takeoffQueue.Enqueue(aircraft);
Console.WriteLine($"飞机 {aircraft.GetCallSign()} 已加入起飞队列,当前队列长度: {takeoffQueue.Count}");
// 如果跑道空闲,处理下一个起飞请求
if (runwayFree)
{
ProcessNextOperation();
}
}
// 处理下一个操作(降落或起飞)
private void ProcessNextOperation()
{
// 降落优先级高于起飞
if (landingQueue.Count > 0)
{
ProcessLanding();
}
else if (takeoffQueue.Count > 0)
{
ProcessTakeoff();
}
}
// 处理降落
private void ProcessLanding()
{
if (landingQueue.Count > 0)
{
runwayFree = false;
Aircraft aircraft = landingQueue.Dequeue();
Console.WriteLine($"管制中心授权 {aircraft.GetCallSign()} 降落");
// 模拟降落过程
aircraft.Land();
// 完成后释放跑道
runwayFree = true;
// 处理下一个操作
ProcessNextOperation();
}
}
// 处理起飞
private void ProcessTakeoff()
{
if (takeoffQueue.Count > 0)
{
runwayFree = false;
Aircraft aircraft = takeoffQueue.Dequeue();
Console.WriteLine($"管制中心授权 {aircraft.GetCallSign()} 起飞");
// 模拟起飞过程
aircraft.Takeoff();
// 完成后释放跑道
runwayFree = true;
// 处理下一个操作
ProcessNextOperation();
}
}
}
// 客户端代码
public class Program
{
static void Main(string[] args)
{
// 创建航空交通管制中心(中介者)
IAirTrafficControl atc = new AirTrafficControlCenter();
// 创建各种飞机(同事)
Aircraft flight1 = new CommercialAircraft("CA1234", atc, 200);
Aircraft flight2 = new CommercialAircraft("MU5678", atc, 180);
Aircraft flight3 = new CargoAircraft("CK8765", atc, 50.5);
// 模拟飞机操作
Console.WriteLine("\n模拟飞机操作:");
// 飞机1请求起飞
flight1.RequestTakeoff();
// 飞机2请求起飞
flight2.RequestTakeoff();
// 飞机1向所有飞机发送消息
flight1.SendMessage("天气晴朗,航行顺利");
// 飞机1请求降落
flight1.RequestLanding();
// 飞机3请求起飞
flight3.RequestTakeoff();
// 飞机2请求降落
flight2.RequestLanding();
}
}
6. 中介者模式的优缺点
6.1 优点
降低了系统的复杂度:将网状结构转变为星型结构,减少了类之间的直接依赖。
增强了系统的可维护性:当一个对象发生变化时,只需要通知中介者,而不用通知其他所有对象。
提高了系统的可复用性:由于同事类与中介者的松散耦合,同事类可以在不同系统中复用。
简化了对象间的协议:对象只需与中介者通信,而不需要了解其他对象的接口。
6.2 缺点
中介者可能过于复杂:随着系统的扩大,中介者可能会变得过于庞大和复杂,形成一个难以维护的"上帝类"。
系统性能可能下降:所有的交互都要经过中介者,可能会导致系统性能下降。
可能导致功能集中:过度使用中介者模式可能导致功能过度集中,违反分布式的设计原则。
7. 中介者模式的变体
7.1 事件聚合器(Event Aggregator)
事件聚合器是中介者模式的一种变体,它允许对象间通过事件机制进行通信,而不需要直接引用彼此。这在大型应用程序中特别有用,可以简化模块间的通信。
using System;
using System.Collections.Generic;
// 事件基类
public abstract class ApplicationEvent
{
// 事件类型
public abstract string GetEventType();
}
// 具体事件:用户登录事件
public class UserLoggedInEvent : ApplicationEvent
{
public string Username { get; private set; }
public UserLoggedInEvent(string username)
{
Username = username;
}
public override string GetEventType()
{
return "UserLoggedIn";
}
}
// 具体事件:消息接收事件
public class MessageReceivedEvent : ApplicationEvent
{
public string Sender { get; private set; }
public string Content { get; private set; }
public MessageReceivedEvent(string sender, string content)
{
Sender = sender;
Content = content;
}
public override string GetEventType()
{
return "MessageReceived";
}
}
// 事件处理器接口
public interface IEventHandler<T> where T : ApplicationEvent
{
void Handle(T eventData);
}
// 事件聚合器(中介者)
public class EventAggregator
{
// 单例实例
private static EventAggregator instance;
// 事件处理器字典
private Dictionary<string, List<object>> handlers = new Dictionary<string, List<object>>();
// 私有构造函数
private EventAggregator()
{
}
// 获取单例实例
public static EventAggregator Instance
{
get
{
if (instance == null)
{
instance = new EventAggregator();
}
return instance;
}
}
// 订阅事件
public void Subscribe<T>(IEventHandler<T> handler) where T : ApplicationEvent
{
string eventType = GetEventTypeName<T>();
if (!handlers.ContainsKey(eventType))
{
handlers[eventType] = new List<object>();
}
if (!handlers[eventType].Contains(handler))
{
handlers[eventType].Add(handler);
}
}
// 取消订阅事件
public void Unsubscribe<T>(IEventHandler<T> handler) where T : ApplicationEvent
{
string eventType = GetEventTypeName<T>();
if (handlers.ContainsKey(eventType))
{
handlers[eventType].Remove(handler);
}
}
// 发布事件
public void Publish<T>(T eventData) where T : ApplicationEvent
{
string eventType = eventData.GetEventType();
if (handlers.ContainsKey(eventType))
{
foreach (var handler in handlers[eventType])
{
((IEventHandler<T>)handler).Handle(eventData);
}
}
}
// 获取事件类型名称
private string GetEventTypeName<T>() where T : ApplicationEvent
{
// 创建类型的实例以获取事件类型名称
// 注意:这不是最优方法,但为了简化示例
T instance = Activator.CreateInstance<T>();
return instance.GetEventType();
}
}
// 登录模块:发布登录事件
public class LoginModule
{
private EventAggregator eventAggregator;
public LoginModule()
{
eventAggregator = EventAggregator.Instance;
}
public void Login(string username, string password)
{
// 在实际应用中,这里会有身份验证逻辑
if (password == "password") // 简化的验证逻辑
{
Console.WriteLine($"用户 {username} 登录成功");
// 发布登录成功事件
eventAggregator.Publish(new UserLoggedInEvent(username));
}
else
{
Console.WriteLine($"用户 {username} 登录失败:密码错误");
}
}
}
// 聊天模块:处理登录事件和发布消息事件
public class ChatModule : IEventHandler<UserLoggedInEvent>
{
private EventAggregator eventAggregator;
private List<string> onlineUsers = new List<string>();
public ChatModule()
{
eventAggregator = EventAggregator.Instance;
// 订阅登录事件
eventAggregator.Subscribe<UserLoggedInEvent>(this);
}
// 处理登录事件
public void Handle(UserLoggedInEvent eventData)
{
onlineUsers.Add(eventData.Username);
Console.WriteLine($"聊天模块:用户 {eventData.Username} 上线,当前在线用户数:{onlineUsers.Count}");
}
// 发送消息
public void SendMessage(string sender, string content)
{
if (onlineUsers.Contains(sender))
{
Console.WriteLine($"聊天模块:用户 {sender} 发送消息:{content}");
// 发布消息接收事件
eventAggregator.Publish(new MessageReceivedEvent(sender, content));
}
else
{
Console.WriteLine($"聊天模块:发送失败,用户 {sender} 不在线");
}
}
}
// 通知模块:处理消息事件
public class NotificationModule : IEventHandler<MessageReceivedEvent>
{
private EventAggregator eventAggregator;
public NotificationModule()
{
eventAggregator = EventAggregator.Instance;
// 订阅消息接收事件
eventAggregator.Subscribe<MessageReceivedEvent>(this);
}
// 处理消息接收事件
public void Handle(MessageReceivedEvent eventData)
{
Console.WriteLine($"通知模块:收到来自 {eventData.Sender} 的新消息通知");
}
}
// 客户端代码
public class Program
{
static void Main(string[] args)
{
// 创建各模块
LoginModule loginModule = new LoginModule();
ChatModule chatModule = new ChatModule();
NotificationModule notificationModule = new NotificationModule();
// 模拟用户登录
Console.WriteLine("模拟用户登录:");
loginModule.Login("alice", "password");
// 模拟发送消息
Console.WriteLine("\n模拟发送消息:");
chatModule.SendMessage("alice", "你好,这是一条测试消息!");
// 模拟未登录用户发送消息
Console.WriteLine("\n模拟未登录用户发送消息:");
chatModule.SendMessage("bob", "这条消息不会被发送,因为bob未登录");
// 模拟另一个用户登录
Console.WriteLine("\n模拟另一个用户登录:");
loginModule.Login("bob", "password");
// 再次发送消息
Console.WriteLine("\n再次发送消息:");
chatModule.SendMessage("bob", "现在我可以发送消息了!");
}
}
7.2 中介者与命令模式结合
中介者模式可以与命令模式结合,形成一种强大的模式组合,尤其适用于复杂的UI系统。命令对象封装操作,而中介者负责协调这些操作之间的关系。
8. 实现中介者模式的最佳实践
避免中介者过于复杂:当中介者变得过于复杂时,考虑将其分解为多个较小的中介者,每个负责特定的功能区域。
使用接口而非具体类:同事对象应该依赖于中介者接口,而不是具体的中介者类,这样可以提高系统的灵活性。
结合事件和委托:在C#中,利用事件和委托机制可以简化中介者模式的实现。
注意中介者的生命周期:确保中介者在其所有同事对象之前创建,并在它们之后销毁。
考虑单例模式:对于系统级别的中介者,可以考虑使用单例模式实现,确保整个系统只有一个中介者实例。
9. 总结
中介者模式是一种强大的行为型设计模式,它通过引入一个中介者对象来减少对象之间的直接依赖,从而降低系统的复杂度。它特别适用于对象间交互复杂的系统,如UI控件交互、聊天应用、航空交通管制等场景。
虽然中介者模式可以有效地减少类之间的耦合,但也需要注意避免中介者本身变得过于复杂。在实践中,可以将中介者模式与其他设计模式(如命令模式、观察者模式等)结合使用,以构建更加灵活和可维护的系统。
理解中介者模式与观察者模式的区别也很重要,这可以帮助我们在适当的场景选择合适的模式。总的来说,当系统中的对象需要复杂的相互交互时,中介者模式是一个值得考虑的解决方案。
参考资料和学习链接
- 设计模式:可复用面向对象软件的基础
- Head First 设计模式
- Refactoring.Guru - 中介者模式
- Dofactory - Mediator Pattern
- Event Aggregator Pattern


浙公网安备 33010602011771号