参考
- Custom container view controllers in Swift
- Logic controllers in Swift
- Model controllers in Swift
- Refactoring Swift code for testability
- iOS 开发中的 Self-Manager 模式
本文旨在总结一些在不对现有项目结构做出重大调整的情况下,逐步改善代码质量及可维护性的方式。
指导思想
将应用程序的各种功能和系统按其职责和关注点明确区分。
目的
我们发明各种架构模式,技术和原理的最终目的都是为了引导我们编写更清晰解耦,易于维护的代码。
Container view controller
视图控制器大量增长的问题在于它们有太多的责任:管理视图,执行布局和处理事件,还要管理网络,图像加载,缓存和许多其他事情。
减少增长的一种方法是将它们分成多个,每个视图控制器负责一个较小的责任区域,然后由一个容器视图控制器控制。
- 内容视图控制器可以专注于布局和渲染特定状态
- 容器视图控制器负责加入和移除内容视图控制器,以及各种内容状态之间的转换
- 这样还有一个好处是UIKit会负责将所有标准UIViewController事件自动发送到子视图控制器
通过嵌套视图控制器,通常最终会使用更加模块化,更简单的实现。
Logic controller
一般来说,想要将大的类型分解为多个部分,可以采用两种方法 - 组合和提取。
上面使用容器视图控制器就是使用组合的方式,但只是这样不能解决所有问题,有些情况还可能增加很多复杂性却只有很少的收益。这种情况下,将功能提取到单独的专用类型可能是更好的选择。
提取,就是将大型部件拉出成一个与原件紧密结合的独立类型。这种方式在各种架构模式很常见 - 比如MVVM,引入View Model类型来处理大部分Model-> View的转换逻辑。
还有一种可以保持MVC模式的简单方法是将视图控制器拆分为视图部分和控制器部分:
- 一个控制器保留UIViewController子类和所有与视图相关的功能
- 另一个控制器与UI分离,专注于处理各种逻辑
- 视图控制器不需要知道其状态是如何加载的,只使用逻辑控制器提供的状态并进行渲染
Self-Manager模式
即赋予一个View更大的权利,让其自己负责自己的所有事件与逻辑。
对于外部使用者来说,只需要简单的传递必要的依赖即可,完全不需要关心和插手任何逻辑。
对于复杂的视图控制器来说,能提取出部分模块进行自管理,甚至复用,可以明显让对应逻辑更清晰,简单。
Model controller
大多数应用程序包含许多不同类型的模型,通常可以分为两类 - 共享的和本地的。
其中共享模型会在许多不同模块中使用,而且通常都会出现一些共享的逻辑。
如果将这些逻辑放在某种形式的单例或全局函数中,通常会损害我们的代码封装,但是直接放到模型中又会很奇怪,因为大多数设计模式都同意的一件事,理想情况下的模型应该是不怎么包含逻辑的简单数据容器。
这个时候可以引入模型控制器,执行模型单个实例相关联的所有逻辑 - 让我们正确地封装模型特定的逻辑:
- 进一步控制和隐藏敏感信息,防止被外部错误使用和修改
- 单一专用代码路径,可以完全单元测试,消除重复逻辑和不一致的风险
合理的依赖注入
要使代码更易于测试时,依赖注入是一个必不可少的工具。
不是让对象创建自己的依赖项或将它们作为单例访问,而是认为对象需要的所有内容都应该从外部传递。
这样既可以更容易地查看给定对象具有哪些确切的依赖关系,还可以使测试变得更加简单 - 因为可以通过模拟依赖关系来捕获并验证状态和值。
尤其是对象内部直接使用单例时,不仅难以测试,而且2者之间可能会产生不明确的关系。