面向对象设计的设计模式(十九):中介者模式

定义

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

适用场景

系统结构可能会日益变得复杂,对象之间存在大量的相互关联和调用,系统的整体结构容易变为网状结构。在这种情况下,如果需要修改某一个对象,则可能会要跟踪和该对象关联的其他所有对象,并进行处理。耦合越多,修改的地方就会越多。

如果我们使用中介者对象,则可以将系统的网状结构变成以中介者为中心的星型结构。中介者承担了中转作用和协调作用,简化了对象之间的交互,而且还可以给对象间的交互进行进一步的控制。

现在我们清楚了中介者模式的适用场景,下面看一下中介者模式的成员和类图。

成员与类图

成员

中介者模式一共有四个成员:

  1. 抽象中介者(Mediator):抽象中介者定义具体中介者需要实现的接口。
  2. 具体中介者(Concrete Mediator):具体中介者实现抽象中介者定义的接口,承担多个具体同事类之间的中介者的角色。
  3. 抽象同事类(Colleague):抽象同事类定义具体同事类需要实现的接口。
  4. 具体同事类(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 <User *>*_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则是同事类的实现。

原文发布于微信公众号 - 程序员维他命(J_Knight_)

原文发表时间:2019-06-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券