面向对象设计的设计模式:中介者模式
作者丨维他命君
来源丨程序员维他命(ID:J_Knight_)
定义
中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,中介者使各对象之间不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
适用场景
系统结构可能会日益变得复杂,对象之间存在大量的相互关联和调用,系统的整体结构容易变为网状结构。在这种情况下,如果需要修改某一个对象,则可能会要跟踪和该对象关联的其他所有对象,并进行处理。耦合越多,修改的地方就会越多。
如果我们使用中介者对象,则可以将系统的网状结构变成以中介者为中心的星型结构。中介者承担了中转作用和协调作用,简化了对象之间的交互,而且还可以给对象间的交互进行进一步的控制。
现在我们清楚了中介者模式的适用场景,下面看一下中介者模式的成员和类图。
成员与类图
成员
中介者模式一共有四个成员:
抽象中介者(Mediator):抽象中介者定义具体中介者需要实现的接口。
具体中介者(Concrete Mediator):具体中介者实现抽象中介者定义的接口,承担多个具体同事类之间的中介者的角色。
抽象同事类(Colleague):抽象同事类定义具体同事类需要实现的接口。
具体同事类(Concrete Colleague):具体同事类实现抽象同事类定义的接口。
模式类图
代码示例
场景概述
模拟一个多人对话的场景:当一个人发出消息后,另外的那些人可以收到该消息。
场景分析
假设一共有A,B,C三个人,那么当A发出消息后,需要分别传递给B,C二人。如果三个人直接相互通信,可能伪代码会是这样的:
A sent message to B
A sent message to C
而且随着人数的增多,代码行数也会变多,这显然是不合理的。
因此在这种场景下,我们需要使用中介者模式,在所有人中间来做一个消息的多路转发:当A发出消息后,由中介者来发送给B和C:
A sent message to Mediator ;
Mediator sent message to B & C
下面我们看一下如何用代码来模拟该场景。
代码实现
首先我们创建通话的用户类User
:
//================== User.h ==================
@interface User : NSObject
- (instancetype)initWithName:(NSString *)name mediator:(ChatMediator *)mediator;
- (void)sendMessage:(NSString *)message;
- (void)receivedMessage:(NSString *)message;
@end
//================== User.m ==================
@implementation User
{
NSString *_name;
ChatMediator *_chatMediator;
}
- (instancetype)initWithName:(NSString *)name mediator:(ChatMediator *)mediator{
self = [super init];
if (self) {
_name = name;
_chatMediator = mediator;
}
return self;
}
- (void)sendMessage:(NSString *)message{
NSLog(@"================");
NSLog(@"%@ sent message:%@",_name,message);
[_chatMediator sendMessage:message fromUser:self];
}
- (void)receivedMessage:(NSString *)message{
NSLog(@"%@ has received message:%@",_name,message);
}
@end
用户类在初始化的时候需要传入中介者的实例,并持有。目的是为了在后面发送消息的时候把消息转发给中介者。
另外,用户类还对外提供了发送消息和接收消息的接口。而在发送消息的方法内部其实调用的是中介者的发送消息的方法(因为中介者持有了所有用户的实例,因此可以做多路转发),具体是如何做的我们可以看下中介者类ChatMediator
的实现:
//================== ChatMediator.h ==================
@interface ChatMediator : NSObject
- (void)addUser:(User *)user;
- (void)sendMessage:(NSString *)message fromUser:(User *)user;
@end
//================== ChatMediator.m ==================
@implementation ChatMediator
{
NSMutableArray *_userList;
}
- (instancetype)init{
self = [super init];
if (self) {
_userList = [NSMutableArray array];
}
return self;
}
- (void)addUser:(User *)user{
[_userList addObject:user];
}
- (void)sendMessage:(NSString *)message fromUser:(User *)user{
[_userList enumerateObjectsUsingBlock:^(User * _Nonnull iterUser, NSUInteger idx, BOOL * _Nonnull stop) {
if (iterUser != user) {
[iterUser receivedMessage:message];
}
}];
}
@end
中介者类提供了addUser:
的方法,因此我们可以不断将用户添加到这个中介者里面(可以看做是注册行为或是“加入群聊”)。在每次加入一个User
实例后,都将这个实例添加到中介者持有的这个可变数组里。于是在将来中介者就可以通过遍历数组的方式来做消息的多路转发,具体实现可以看sendMessage:fromUser:
这个方法。
到现在为止,用户类和中介者类都创建好了,我们看一下消息是如何转发的:
ChatMediator *cm = [[ChatMediator alloc] init];
User *user1 = [[User alloc] initWithName:@"Jack" mediator:cm];
User *user2 = [[User alloc] initWithName:@"Bruce" mediator:cm];
User *user3 = [[User alloc] initWithName:@"Lucy" mediator:cm];
[cm addUser:user1];
[cm addUser:user2];
[cm addUser:user3];
[user1 sendMessage:@"happy"];
[user2 sendMessage:@"new"];
[user3 sendMessage:@"year"];
从代码中可以看到,我们这里创建了三个用户,分别加入到了聊天中介者对象里。再后面我们分别让每个用户发送了一条消息。我们下面通过日至输出来看一下每个用户的消息接收情况:
[13806:1284059] ================
[13806:1284059] Jack sent message:happy
[13806:1284059] Bruce has received message:happy
[13806:1284059] Lucy has received message:happy
[13806:1284059] ================
[13806:1284059] Bruce sent message:new
[13806:1284059] Jack has received message:new
[13806:1284059] Lucy has received message:new
[13806:1284059] ================
[13806:1284059] Lucy sent message:year
[13806:1284059] Jack has received message:year
[13806:1284059] Bruce has received message:year
下面看一下上面代码对应的类图。
代码对应的类图
优点
中介者使各对象不需要显式地相互引用,从而使其耦合松散。
缺点
在具体中介者类中包含了同事类之间的交互细节,可能会导致具体中介者类非常复杂,使得其难以维护。
iOS SDK 和 JDK中的应用
JDK中的
Timer
就是中介者类的实现,而配合使用的TimerTask
则是同事类的实现。
-End-
最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!
面试题
】即可获取