Coordinator 系列之 Coordinator 模式的起源

3,002 阅读5分钟

翻译原文


视图控制器最大的问题之一是它们混合了您的导航逻辑、视图逻辑和业务逻辑。

当一个 tableViewCell 被选中时,代理方法通常会是这样的:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {  
	id object = [self.dataSource objectAtIndexPath:indexPath];  
	SKDetailViewController *detailViewController = [[SKDetailViewController alloc] initWithDetailObject:object]  
	[self.navigationController presentViewController:detailViewController animated:YES];  
}

三行简单的代码做了三件事:获取模型对象、创建视图控制器、展示视图控制器。在一个相当简单的 App 中,这样完全没有问题。每个视图控制器大概率只会使用一次。像这样耦合它们并不是危险的操作。然而当你的App日益复杂,一个控制器可能会在一个新的地方被使用。也许会被适配到iPad上,也许在APP extension中,也许是一个老业务的新逻辑。

如果导航逻辑被写在了视图控制器中,那么这个控制器就没法不通过拖拽所有的代码来复用它已有的视图显示逻辑。不要忘记,视图控制器的基类是以UI为前缀,这说明它是视图对象,处理用户逻辑超出了它的能力范围。

在上面的代码中,视图控制器知道了如何创建整个业务流中的下一个对象以及如何显示它。在第三行代码中,它告诉它的父视图控制器要做什么,这显然是颠倒黑白的(译者:相当于儿子指挥老子做事情)。更糟糕的是,类似的代码分布在多个视图控制器中,每个视图控制器只知道如何执行下一步。

我过去认为视图控制器是 App 中最上层的东西,是知道如何运行整个流程的东西。然而,最近我发现让一个更高级别的对象来指挥视图控制器有很多好处,这个对象的作用是引领和管理其权限内的所有视图控制器。

我把这些对象叫做 Coordinators,或者也可以叫做 Directors。要真正很好地执行此模式,您需要一个高级协调器来指导整个应用程序(有时称为应用程序控制器模式)。AppDelegate持有AppCoordinator,每个协调器拥有一组子协调器。特别是如果你有不止一个控制器,如在标签栏应用,每个导航控制器有自己的协调器,用于指导其行为和导航流。可以为注册或创建内容等特定任务创建更多的子协调器。每个协调器由其父协调器创建。在开发过程的早期使用此模式,即使在单步任务(如身份验证)中也很有用。

协调器是一个 PONSO(Plain Old NSObjects)纯粹的NSObject对象。对于 Instagram 的照片创作流程,我们可以使用PhotoCreationCoordinator。app协调器可以生成一个新视图,并将根视图控制器传递给它,这样它就可以在流中呈现第一个视图控制器。

- (void)beginPhotoCreationProcess {  
	PhotoCreationCoordinator *coordinator = [[PhotoCreationCoordinator alloc] initWithRootViewController:self.rootViewController delegate:self]  
	[self.childCoordinators addObject:coordinator];  
	[coordinator beginPhotoCreationProcess];  
}
	
- (void)photoCreationCompletedSuccessfully:(PhotoCreationCoordinator *)coordinator {  
	[self.childCoordinators removeObject:coordinator];  
}
	
- (void)photoCreationCanceled:(PhotoCreationCoordinator *)coordinator {  
	[self.childCoordinators removeObject:coordinator];  
}  

协调器的-beginPhotoCreationProcess方法现在可以创建一个新的照片选择视图控制器,并根据需要配置它。协调器可以成为照片选择视图控制器的委托,以便它可以在显示下一步的时候得到通知。流程中的任何视图控制器都不需要知道任何其他视图控制器。每个视图控制器都是一个孤岛。

业务逻辑(例如发布照片)被包装在属于其自己的对象中,可以根据需要向上挪到协调器或向下挪到模型。无论哪种方式,它都从视图控制器移出去了。将其挪到协调器是非常好的,因为协调器已经充当代码不同部分之间的粘合剂。

将流提取到协调器中有无数好处。视图控制器现在可以专注于将模型绑定到视图。可以更容易地重用它们,比如在扩展或非绑定应用程序中,而不需要复杂的条件来管理不同的上下文。A/B 测试非常简单,只需创建一个不同的协调器对象并使用它来启动流程。导航逻辑现在有了家,有家的感觉真的很好。

视图控制器的初始化也被提取出来了。这一点不容忽视。初始化总是一个看起来简单实则更复杂的任务,需要大量关于类和配置的知识,我们将它转移到一个更好的地方,在那里可以做出更明智的决定。

不知不觉协调器对象就会持有许多分离的状态,换句话说,特别是对于iPhone应用程序,随时都只能看到一个屏幕。这使它们水到渠成地成为使用状态机来管理其所有数据的合适场所。(译者注:状态机的最佳实现就是单向数据流,之后译者会引申出来新的模式)。

苹果喜欢让视图控制器成为 iOS 世界的中心。这一点在 iOS 8 的新UISplitViewController api以及 storyboard segue等 Interface Builder 组件中很明显。不幸的是,以视图控制器为中心的应用程序开发方法是不可伸缩的。协调器是一种很好的方法,可以将代码隔离成小的、易于替换的块,成为解决视图控制器问题的解决方案的一部分。