中介者模式
中介者模式是对象行为型模式,它通过将一系列对象之间的交互进行封装,使得之前混乱无需的网状的依赖关系,变成一种星状的依赖关系,使得每个对象之间可以独立的改变它们的交互。中介者模式也是迪米特法则的经典运用。
在现实生活中,这样的运用非常常见,如聊天时,信息需要经过服务器,此时服务器就是一个中介者。GUI元素的大量交互也是通过控制器去完成的,而控制器就是一个中介者。中介者模式将原本互相依赖的对象,相互独立,它们之间的交互需要强行使用中介者来进行,从而保证对象的独立,降低耦合。
中介者模式将多个对象的网状关系网封装,从而使得类所拥有的依赖减少,使得易于修改,扩展和复用。
结构
说明
- 抽象同事类(Colleague)- 定义同事接口类,保存一个中介者类的引用,提供同事类对象交互的抽象方法。
- 具体同事类(Concrete Colleague)- 实现抽象方法,可以通过中介者与其他对象进行交互。
- 抽象中介者类(I Mediator)- 声明同事类交流的方法,一般通常包含一个通知方法。组件将任意上下文对象(也可以是自己),作为方法参数,这样来保证接收者和发送者不发生耦合。
- 具体中介者(Concrete Colleague)- 封装多个组件间的关系。具体中介者通常会保存所有组件的引用并对其进行管理。
实现
实现一个简单的聊天发送
聊天室(抽象同事类)
public abstract class ChatRoom
{
private IMediator _mediator;
protected ChatRoom(IMediator mediator)
{
_mediator = mediator;
}
public void Send(string message) => _mediator.Send(message, this);
public abstract void Receive(string message);
}
普通聊天室(具体同事类)
public class OrdinaryChatRoom : ChatRoom
{
private TMP_Text _text;
private string _name;
public OrdinaryChatRoom(IMediator mediator, TMP_Text text, string name) : base(mediator)
{
_text = text;
_name = name;
_text.text = name + "\n";
}
public override void Receive(string message)
{
_text.text += message + "\n";
}
}
中介接口(抽象中介者)
public interface IMediator
{
//发送
void Send(string message, ChatRoom room);
}
聊天室中介(具体中介)
public class ChatRoomMediator : IMediator
{
private List<ChatRoom> _rooms;
public ChatRoomMediator()
{
_rooms = new List<ChatRoom>();
}
public void RegisterChatRoom(ChatRoom room) => _rooms.Add(room);
public void RemoveChatRoom(ChatRoom room) => _rooms.Remove(room);
public void Send(string message, ChatRoom room)
{
foreach (var chatRoom in _rooms.Where(r => r != room))
{
chatRoom.Receive(message);
}
}
}
客户端代码
public class MediatorExample : MonoBehaviour
{
[SerializeField] private TMP_Text _text1;
[SerializeField] private TMP_Text _text2;
[SerializeField] private TMP_Text _text3;
private ChatRoom _room1;
private ChatRoom _room2;
private ChatRoom _room3;
private void Awake()
{
//创建中介者
ChatRoomMediator mediator = new ChatRoomMediator();
//创建聊天室
_room1 = new OrdinaryChatRoom(mediator, _text1, "No1");
_room2 = new OrdinaryChatRoom(mediator, _text2, "No2");
_room3 = new OrdinaryChatRoom(mediator, _text3, "No3");
mediator.RegisterChatRoom(_room1);
mediator.RegisterChatRoom(_room2);
mediator.RegisterChatRoom(_room3);
}
private void OnGUI()
{
//三个按钮,分别代表 每个聊天室的信息发送
if (GUILayout.Button("No1", GUILayout.Width(150), GUILayout.Height(75)))
{
_room1.Send("No1 Information");
}
if (GUILayout.Button("No2", GUILayout.Width(150), GUILayout.Height(75)))
{
_room2.Send("No2 Information");
}
if (GUILayout.Button("No3", GUILayout.Width(150), GUILayout.Height(75)))
{
_room3.Send("No3 Information");
}
}
}
应用场景
- 当一些对象和其他对象发生紧密耦合时,难以维护
- 组件由于依赖其他组件无法复用
- 希望有一个运行于多个类中间的对象
优缺点
优点
- 将多个类间的交流抽取到一起,使其便于维护和理解
- 降低了类对象之间的依赖,降低耦合
缺点
- 中介类本身是为了减低耦合而产生的,但由于包含了其他对象,当对象数量过多时,会导致中介类变得臃肿,且可能成为上帝对象。
与其他模式的关系
- 责任链,命令,中介者,观察者都是在处理请求者和执行者的关系:责任链将一系列执行者连接起来,使其可以动态传递给每个执行者进行处理或被其中一名潜在的执行者处理。命令模式是将请求者和执行者之间的调用抽取出来,成为一个参数化对象的命令,对通过管理这些命令来完成对执行者的调用。中介者模式通过中介对象,来使得原本直接依赖的各个对象,请求者和执行者通过中介者来进行交流,从而降低耦合。观察者模式是将请求者的请求与执行者对象动态绑定。
- 外观与中介者的职责十分类似,它们都尝试在大量紧密耦合的类中组织起合作。外观类是将多个子系统对象封装,从而提供一个简单的接口,方便使用,本身不做任何新的功能。子系统本身是不会意识到有外观类的存在,子系统之间是可以正常交互的。中介者类则是将对象的交互中心化,各对象只知道中介者对象,通过中介者对象进行交互。一个是对结构的封装,一个是关注对象的交互。
- 观察者和中介者在使用时需要注意,中介者旨在消除对象间的互相依赖,观察者是为了在对象间建立动态的连接。而在使用时,中介者模式可以使用类似于观察者模式的方式,实现中介者对象。即将每个对象都变为一个观察者和发布者。它们即是发布者,也可以作为接收者。实现分布式观察者模式。但这只是中介者模式中的其中一部分。