iOS中的依赖性太多?使用组合根模式

1,360 阅读3分钟

iOS中的依赖性太多?使用组合根模式

iOS架构不仅仅是MVC、MVVM和M*。

如果你问一个iOS工程师,"你将使用哪种架构来设计X应用程序?",他们通常会回答MVC、MVVM、MVP等等。

这篇文章将解释为什么这些流行的缩略语不能完全回答这个问题,并将展示一个替代的解决方案。

MVC、MVVM、MVP是UI层设计或UI架构,而不是系统架构。

它们描述了数据的流动和UI层内的责任分离。它并没有回答任何关于导航、网络、缓存、业务逻辑等问题。

将这些责任添加到UI层中会产生具有大量依赖关系图的单体应用。这导致了重写、无法测试的代码和高成本的变化。

解决办法是将单体分解成模块化的组件,并在 "组合根**"**处将它们组合在一起。例如,你想创建一个类似于Instagram的图片feed应用。

一个常见的用户界面架构可能是这样的。

用户选择一个FeedImageCell ,并期望导航到一个FeedDetailedViewController

上面的代码是一个常见的策略,要求视图控制器负责导航和创建其子视图控制器。

现在想象一下,你的需求发生了变化,你现在需要实现记录、从远程或本地资源库检索资源以及支持旧操作系统的能力。

你使用URLSession ,在viewDidLoad ,如下图所示,加载数据。

当用户选择一个项目时,你检查以确定导航到哪个控制器并记录它。

这些微小的变化需要开发人员修改FeedViewController 。这违反了Open/Close原则,因为可能的导航路线在编译时就已经很早确定。

即使我们通过构造注入将依赖关系注入到延迟行为的FeedViewController ,我们仍然需要注入它创建的任何子视图控制器的依赖关系。

如果子视图控制器有子视图控制器,我们就必须在根视图控制器上注入这些责任,并将它们层叠下去。这可能会在大型复杂的应用程序中无限地持续下去,并在大规模的依赖关系图中产生重大问题。

通过与LoggingFrameworkURLSession 耦合,你已经可以看到围绕FeedViewController 的依赖网络。它的任何子节点的依赖性也将成为它的依赖性。

迎接组合根 模式

组成根模式延迟了关于组件如何交互的决策,并允许我们拦截和修改行为。

它存在于Main 模块中,这个概念对大多数iOS工程师来说是陌生的。

让我们来看看如何在主模块中组合组件并打破单体。

现在,FeedViewController 不再负责创建和导航它的孩子。

FeedComposer 组合了FeedViewController ,并将其与所有的依赖关系组装起来。使用闭包,我们可以注入行为,而无需将UI模块组件与NetworkingLogging 模块耦合。

FeedNavigation 处理路由,Logging 模块通过其委托被通知。

注意依赖关系的流程。主模块依赖于所有其他模块,而没有一个模块知道彼此的情况。

通过创建模块化代码并在组合根部将组件组合在一起,我们在模块之间创造了虚拟边界。

假设我们希望我们的Feed应用程序能够在Apple Watch或Mac OS上运行。最初的方法是在UI层中耦合所有的东西,并称其为MVC/MVVM/M*,这需要完全重写。

用我们的新方法,我们只需要重写FeedUI 模块,并在Main中的组成根部连接组件。所有的业务逻辑、网络、日志都将被重新使用。这就是模块化设计的力量。

谢谢你的阅读。完整的源代码可以在GitHub仓库中找到。