Mediator(中介者)模式在iOS开发当中的使用

首先来看看wiki上对中介者模式的解释:

In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.

在软件工程中,中介模式定义了一个对象,该对象封装了一组对象是如何交互的。这种模式被认为是一个行为模式,因为它可以改变程序的运行行为。

在一个项目里,我们开发的程序是由大量的类来组成的,随着程序功能的不断增加,类和类之间的依赖关系也跟着趋于复杂,而中介者模式便能解决这个问题。

这种复杂的依赖关系,在iOS开发当中常用的MVC模式中定义的ViewController(VC)就是典型的代表。一般情况下一个页面便对应代码中的一个VC,而一个中等规模的软件至少会有几十个的页面,对应的就是几十个VC。(当然你也可以说复用VC,但是在正常情况下,VC的复用场景会有这么多么?这里我们不考虑复用VC的情况。)而要管理这些VC之间的关系是一件非常繁琐的事情,我们要处理各个VC之间的关系,每当一个VC要跳转到另外个VC,我们需要包含新的VC的头文件,于是有的起衔接作用的页面中包含了大量的其他VC的头文件。同时每当产品经理要求修改一些页面的逻辑关系,我们又需要对这些头文件和对应的代码进行修改,想想我就头大。类似下图:

而使用中介者模式可以非常好地去解决这个问题。实现一个管理VC关系的功能类,这个类的作用是给每个VC绑定一个URL,当需要打开某一个新的VC时,通过功能类的openURL接口传入新VC的URL即可。发起openURL的一方不需要去依赖新的VC,只需要和功能类建立联系,类似下图:

Mediator类的作用就相当于一个路由器(Router),将客户发起的URL请求转移到对应的类。我们还可以利用iOS应用本身的特点,以及Objective-C语言的特性,给这个Router增加一些功能: 在一些特殊情况下,打开不同的页面(VC)之后,还需要传递一些命令到这个VC,让其做一些特殊的操作,于是可以通过配置URL的参数,再通过参数名和参数值传递给新VC:

[MRRouter openURL:@"scheme://test?aa=11&bb=22"];
复制代码

更方便的,可以直接用字典来传递参数:

[MRRouter openURL:@"scheme://test3" parameters:@{@"ccc":@"333",@"ddd":@"444"}];
复制代码

于是还需要创建一个映射表,来建立URL和VC之间的映射关系,每增加一个VC,就在这个表里头增加一个对应关系。这里可以利用Objective-C的Runtime来动态地根据类名去映射,简化我们的日常编码操作,具体的实现就不在这里详细描述了,有兴趣可以去这里看具体的实现。

还可以定义一个默认的openURL操作:

[MRRouter sharedInstance].defaultExecutingBlock = ^(id object, NSDictionary *parameters) { 
    [self.navigationController pushViewController:object animated:YES];
};
复制代码

这样在一般情况下打开一个新的页面,不需要引入任何头文件,也不需要建立映射关系,只需要调用Router的openURL接口。

通过以中介者模式为基础的Router管理页面的额外好处,就是可以简化服务端push需要打开新页面的操作,当有一个新的活动,要打开一个对应的页面,只需要把这个页面的URL通过push发送给客户端,客户端直接传递给Router即可,省略了大量的if/else的逻辑判断。

总结:

中介者模式的使用不光用在VC的管理,当功能中出现了类似“多对多”的复杂的对象群时,就可以用到它来管理这些对象。当然,再次之前,你需要考虑的不应该是开始使用中介者模式,而是考虑这个功能的设计是否合理。 使用中介者模式虽然降低了各个对象之间的耦合,减少了对象之间逻辑的复杂度,但是这个复杂度在一定程度上转移到了Mediator类中,因此Mediator类的功能维护需要谨慎处理。 以上举例中的所有代码可以在这里下载,欢迎各种star/request/issue。