定义
就拿大学里的班级来说,,假如大家都使用加QQ好友的方式来同学,那么班级间的通信方式主要如下所示的网状图所示:
可以从上图看出,该网状结构是比较复杂的,每个成员都存在直接的联系,所以关系复杂,用面向对象的设计语言来说就是耦合性太高。每个同学之间都要相互间加好友,光是问QQ号就够麻烦了,假如所有班级同学都加了好友的话,那么要是再转入一个漂亮妹子怎么办?岂不是大家都得更新下QQ好友列表,关键的是,妹子需要加整个班的人的QQ,不管男女老少,这真是一个很艰巨的任务。但是实际上,我们现在的做法一般都是建个QQ班级群,然后班长或者啥的把QQ群号码公布一下,然后大家自己加进去就好了。这样,相互间就不用交换QQ号码了,然后大家加不加好友的都可以直接进行通讯了。使用QQ群之后,班级间的主要通信方式变成下面的星形结构了:
如上图所示,星形的通信结构图明显比网状的结构图简单清晰明了的多,而且扩展性好,因为如果新转来了一位同学,我们只需要把该同学拉入班级群即可,队员原本班级群的人来说不需要做任何额外的工作就可以和该新转来的同学进行通信了。这里使用的QQ班级群承担的角色就是本章所要说的中介者(Mediator),而使用班级QQ群,就类似地使用了中介者设计模式。
所谓中介者设计模式,就是定义一个用于封装一系列子对象内部进行交互的对象。中介者设计模式使得对象间不再相互引用,使得每个对象间的引用分离独立出来,降低类之间的耦合性。其原定义如下:
中介者设计模式的类图结构示意图如下所示:
上图中,抽象类Mediator定义了Colleague间交互的通用接口,Colleague对象都会有一个Mediator类型的引用对象,该引用对象掌握了所有Colleague对象,因此他可以协调Colleague间的交互。还是拿QQ班级群的例子来说,我们可以发现,作为中介者的班级QQ群,他会出现在每个班级QQ群成员的“我的QQ群”列表中,这就相当于每个Colleague对象有一个中介者的引用对象一样。实际上,上图对应的对象结构图应该是这样子的:
每个对象都通过自己的内部中介者引用对象和其他对象交互,而不是对象间直接交互。
适用场景
- 一些列相互独立的对象虽然对象接口设计精良,但是由于他们之间需要相互调用,所以变得很难使用
- 当一个对象因为引用或者和其他对象有太多联系的而变得难以复用的时候
- 如果不允许使用大量子类化,而且又需要自定义化一个分布在多个类的某个逻辑或者方法的话
当出现以上所述的情况后,你就可以考虑使用中介者模式来设计你的程序了。
代码示例
为了简单,也为了偷懒,用最上面的例子来捡代码做个例子吧。不过为了区别各个子对象的差异性,每个同学都是不同类型的对象,而且获取QQ号的接口各不一样。
同学A的类定义如下所示:
//
// ClassMate.h
// MeditorDemo
//
// Created by God Lin on 15/1/21.
// Copyright (c) 2015年 arbboter. All rights reserved.
//
#import <Foundation/Foundation.h>
@class ClassQQTeam;
@interface ClassMateA : NSObject
{
@private
ClassQQTeam* _classQQTeam;
NSString* _qqNumberA;
}
@property (nonatomic, strong) ClassQQTeam* classQQTeam;
@property (nonatomic, strong) NSString* qqNumberA;
@end
//
// ClassMate.m
// MeditorDemo
//
// Created by God Lin on 15/1/21.
// Copyright (c) 2015年 arbboter. All rights reserved.
//
#import "ClassMateA.h"
@implementation ClassMateA
- (id) init
{
if(self = [super init])
{
_qqNumberA = @"123426";
}
return self;
}
@end
为了示范多个交互,还需要创建其他两位同学,分别是同学B和同学C,其实现和同学A的大致一样,不过获取QQ号的接口分别是 qqNumber和 qqNumberC。其实现如下所示:
#import "ClassMateB.h"
@implementation ClassMateB
- (id) init
{
if(self = [super init])
{
_qqNumberB = @"123427";
}
return self;
}
@end
#import "ClassMateC.h"
@implementation ClassMateC
- (id) init
{
if(self = [super init])
{
_qqNumberC = @"123428";
}
return self;
}
@end
注意,上述三位同学都有一个ClassQQTeam的引用,该类型即为中介者,其定义和实现如下:
//
// ClassQQTeam.h
// MeditorDemo
//
// Created by God Lin on 15/1/21.
// Copyright (c) 2015年 arbboter. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface ClassQQTeam : NSObject
- (NSString*) getQQNumber:(id)claseMate;
@end
//
// ClassQQTeam.m
// MeditorDemo
//
// Created by God Lin on 15/1/21.
// Copyright (c) 2015年 arbboter. All rights reserved.
//
#import "ClassQQTeam.h"
#import "ClassMateA.h"
#import "ClassMateB.h"
#import "ClassMateC.h"
@implementation ClassQQTeam
- (NSString*) getQQNumber:(id)claseMate
{
// 因为中介者知道了解各个对象,所以可以直接调用对方接口
NSString* qqNumber = nil;
if([claseMate isKindOfClass:[ClassMateA class]])
{
ClassMateA* mate = (ClassMateA*)claseMate;
qqNumber = mate.qqNumberA;
}
else if ([claseMate isKindOfClass:[ClassMateB class]])
{
ClassMateB* mate = (ClassMateB*)claseMate;
qqNumber = mate.qqNumberB;
}
else if ([claseMate isKindOfClass:[ClassMateC class]])
{
ClassMateC* mate = (ClassMateC*)claseMate;
qqNumber = mate.qqNumberC;
}
return qqNumber;
}
@end
中介者利用自身优势,统筹兼顾,提供获取了QQ得统一接口,现在,同学间交互不需要直接和对方交流了,只用利用引用中介者就可以了,测试代码如下所示:
int main(int argc, const char * argv[])
{
// 创建中介者
ClassQQTeam* qqTeam = [[ClassQQTeam alloc] init];
// 创建系统各个子对象并初始化各个对象对中介者的引用
ClassMateA* aMate = [[ClassMateA alloc] init];
aMate.classQQTeam = qqTeam;
ClassMateB* bMate = [[ClassMateB alloc] init];
bMate.classQQTeam = qqTeam;
ClassMateC* cMate = [[ClassMateC alloc] init];
cMate.classQQTeam = qqTeam;
// 如果子对象可以通过中介者获取其他对象的QQ号,
// 而不需要直接和对应的对象直接产生联系
// 以ClassMateA对象为例
NSLog(@"bMate's QQ number is : %@", [aMate.classQQTeam getQQNumber:bMate]);
NSLog(@"cMate's QQ number is : %@", [aMate.classQQTeam getQQNumber:cMate]);
return 0;
}
输出结果如下:
2015-01-21 22:31:05.850 MeditorDemo[19645:35199530] bMate's QQ number is : 123427
2015-01-21 22:31:05.852 MeditorDemo[19645:35199530] cMate's QQ number is : 123428
Program ended with exit code: 0
可以发现,同学A在直接和其他同学的交互下,通过中介者QQ群直接获取和对方的QQ号的,so, it works!
总结
中介者模式是为了降低对象间的耦合性而提出来的,用以集中管理对象间的通信。