正文
什么是 Coordinator
Coordinator 是 Soroush Khanlou 在
一次演讲
中提出的模式,启发自
Application Controller Pattern
。
先来看看传统的作法到底存在什么问题。
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = self.dataSource[indexPath.row]
let vc = DetailViewController(item.id)
self.navigationController.pushViewController(vc, animated: true, completion: nil)
}
再熟悉不过的场景:点击
ListViewController
中的 table 列表元素,之后跳转到具体的
DetailViewController
。
实现思路即在
UITableViewDelegate
的代理方法中实现两个 view 之间的跳转。
传统的耦合问题
看似很和谐。
好,现在我们的业务发展了,需要适配 iPad,交互发生了变化,我们打算使用 popover 来显示 detail 信息。
于是,代码又变成了这个样子:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = self.dataSource[indexPath.row]
let vc = DetailViewController(item.id)
if (! Device.isIPad()) {
self.navigationController.pushViewController(vc, animated: true, completion: nil)
} else {
var nc = UINavigationController(rootViewController: vc)
nc.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = nc.popoverPresentationController
popoverContent.preferredContentSize = CGSizeMake(500, 600)
popover.delegate = self
popover.sourceView = self.view
popover.sourceRect = CGRectMake(100, 100, 0, 0)
presentViewController(nc, animated: true, completion: nil)
}
}
很快我们感觉到不对劲,经过理性分析,发现以下问题:
-
view controller 之间高耦合
-
ListViewController 没有良好的复用性
-
过多 if 控制流代码
-
副作用导致难以测试
Coordinator 如何改进
显然,问题的关键在于「解耦」,看看所谓的 Coordinator 到底起到了什么作用。
先来看看 Coordinator 主要的职责:
-
为每个 ViewController 配置一个 Coordinator 对象
-
Coordinator 负责创建配置 ViewController 以及处理视图间的跳转
-
每个应用程序至少包含一个 Coordinator,可叫做 AppCoordinator 作为所有 Flow 的启动入口
了解了具体概念之后,我们用代码来实现一下吧。
不难看出,Coordinator 是一个简单的概念。因此,它并没有特别严格的实现标准,不同的人或 App 架构,在实现细节上也存在差别。
但主流的方式,最多是这两种:
-
通过抽象一个 BaseViewController 来内置 Coordinator 对象
-
通过 protocol 和 delegate 来建立 Coordinator 和 ViewController 之间的联系,前者对后者的「事件方法」进行实现
由于个人更倾向于低耦合的方案,所以接下来我们会采用第二种方案。
事实上 BaseViewController 在复杂的项目中,也未必是一种优秀的设计,不少文章采用 AOP 的思路进行过改良。