Clean Swift

Clean Swift.png

本文翻译自 Clean Swift

GitHub Demo

过去两年,有许多关于 VIPER 的文章,这是一种在iOS项目中十分流行的架构模式。 如果你对它还不了解,可以看看 这篇文章

今天,我想谈谈代替 VIPER 的另一个选择 -- Clean Swift

首先,Clean Swift 和 VIPER 有些相似;然而,在查看它们模块间交互的方式后,两者的区别也显而易见。在 VIPER 中,模块交互的基础是 Presenter,Presenter 将用户的请求传递到 Interactor 处理,Interactor 处理完成后将结果返回给 Presenter,Presenter 把结果格式化为 View Controller 要展示的形式,并返回给 View Controller:

VIPER VIP.png

在 Clean Swift 中,主要的模块是 View ControllerInteractorPresenter,和 VIPER 有点相似:

Clean Swift VIP.png

但与 VIPER 不同的是,在 Clean Swift 中,不同模块之间的交互是发生在一个圆形循环中。数据的传递方式基于协议(VIPER 也是如此),采用协议的好处是 允许我们在需要的时候将此系统中的一个组件,替换为另一个遵守了相同协议的对象。它们之间的交互处理过程通常像这样:用户点击一个按钮,View Controller 创建一个携带相关信息的对象,把它送给 Interactor。Interactor 则根据业务逻辑启动特定的场景,得到结果并传给 Presenter。Presenter 将结果格式化为需要展示的样式,再发给 View Controller 展示。让我们来仔细看看 Clean Swift 的各个模块吧。

View (View Controller)

View Controller 负责配置所有视图相关的属性(与 VIPER 相似),像颜色、UILabel 的样式或者布局。因此,在 Clean Swift中的每个 UIViewController 都实现了一个特定的输入协议(DisplayLogic)来展示数据 或 展示用户的操作结果。

Interactor

Interactor 包含了所有的业务逻辑。它接收来自 View Controller 的用户的操作事件及参数(例如:输入框文本的变化 或 按钮的点击)。这些事件被定义在 Interactor 的输入协议中(BusinessLogic)。在处理完成后,Interactor 将结果传递给 Presenter,Presenter 会格式化结果并传递给 View Controller

Clean Swift 中,Interactor 只会接收来自 View Controller 的请求。然而,而在 VIPER 中,这些请求将通过 Presenter 作为中间层来传递。

Presenter

Presenter 会准备好展示给用户的数据并输出到 View Controller。这些输出的结果会遵守 View Controller 的输入协议(DisplayLogic)。例如,Presenter 可以改变文本的格式,将枚举的颜色转换为 RGB,等等。

Worker

为了避免繁琐的业务细节和重复的逻辑导致 Interactor 过于复杂和庞大,你可以添加额外的 Worker 模块。对于简单的项目来说,Worker 并非是必要的选择,但是在复杂的场景下,它将为 Interactor 分担部分任务。例如:Worker 可以实现与数据库交互的逻辑,特别是当程序的不同地方使用相同的查询时。

Router

Router 的职责是在不同界面之间跳转和传递数据。它引用了 View Controller,这是因为在 iOS 系统上界面跳转一直是 View Controller 的职责。如果你是用 segues,则可以通过在 PrepareForSegue 方法中调用 Router 方法来简化界面跳转的初始化,因为 Router 已经知道如何传输数据并在没有 Interactor / Presenter 的情况下完成此操作。使用 Interactor 中实现的每个界面的 DataStore 协议传递数据,该协议还限制了从路由器访问界面内部数据的能力。

Models

Models 是纯数据结构,它描述了在不同模块间数据传递的信息。业务逻辑(BusinessLogic)的每个实现函数都有自己的 Model。Request 用于从 View Controller 向 Interactor 发送请求。Response 是 Interactor 对 Presenter 的响应。ViewModel 描述了从 Presenter 传输到 View Controller 进行显示的数据。

Example

让我们用一个简单的 例子 来研究一下这个架构。这是一个简化表示 ContactBook 的应用程序,但它足以让我们理解 Clean Swift 的基础。这个 app 包括联系人列表,以及添加和编辑联系人的功能。

每个 View Controller 包含了一个实现了业务逻辑协议(BusinessLogic)的对象,叫做 Interactor,也包含一个 Router 对象,它实现了界面间数据传输和跳转的协议。

你可以在 View Controller 中用一个单独的私有方法中配置 Interactor 和 Router;或者,有些开发者认为 View Controller 不需要参与此配置,你也可以创建一个 Configurator 单例来将这部分的配置代码抽离出 View Controller,并且 Configurator 不应访问 View Controller 中的其他部分代码。Configurator 类并不存在于 Uncle Bob's clean architecture 的描述中,也不出现在经典的 VIPER 架构中。在添加联系人的界面使用 Configurator 如下所示:

Configurator 包含一个配置方法,与 View Controller 中的配置方法相同。

[译者注]:Clean Swift 已不使用 Configurator 了,取而代之的是在 View Controller 中使用 setup() 来配置]

详见:Zero configuration

View Controller 实现中的另一个重点是 prepareForSegue() 方法中的代码:

细心的读者可能已注意到 Router 被要求遵守 NSObjectProtocol。这样做是为了在使用 segue 时,我们可以使用此协议的标准方法进行路由。为了支持这种简单的重定向,segue 标识符的命名必须与 Router 方法名称的结尾完全相同。例如:点击 cell 跳转查看联系人界面,在 Storyboard 中有一个 segue 与之关联,它的标识符是 “ViewContact”,那么,在 Router 中就应该有一个相应的 “routeToViewContact()” 方法。

Interactor 请求数据用来展示看起来也很容易:

让我们看一下 Interactor。Interactor 实现了 ContactListDataStore protocol,这个协议描述了存储和访问的数据。在我们的例子中,它只是一个联系人数组,用 getter 方法限制了它在 Router 中不能被其他 模块修改。

这是我们联系人列表 业务逻辑协议的实现:

它从 ContactListWorker 接收 联系人数据。在这个例子中,Worker 负责数据的加载方式。Worker 可以访问第三方服务,例如,决定是从缓存中获取数据还是从网络下载。Interactor 收到数据之后,发送一个 response 给 Presenter 来准备要展示的数据。Interactor 包含对 Presenter 的引用,用于实现这个功能。

Presenter 只实现了一个协议 -- ContactListPresentationLogic,在我们的例子中,它将联系人的姓名和姓氏的首字母改为大写,然后形成 DisplayedContact 的数据模型,并将其传递给 View Controller 以显示。

至此,VIP 循环就完成了,View Controller 展示数据,实现协议 ContactListDisplayLogic 的方法。

以下是显示联系人的数据模型:

在这种情况下,查询不包含查询参数,因为它只是一个典型的联系人列表。但是,如果联系人列表界面包含过滤等限制,则可以将过滤参数添加到此查询请求中。Interactor 的 response model 包含了必要的联系人数组,ViewModel 也由用于展示的 DisplayedContact 数组组成。

为什么要使用 Clean Swift 呢?

让我们思考一下这个架构的优缺点。

首先,Clean Swift 有模板代码使得我们很容易就可以创建一个模块。这些模板代码可以针对各种体系结构编写,但是当它们开箱即用时,可以为你节省几个小时的时间。

第二,这个架构,包括 VIPER,都是容易测试的。任何模块都可以通过 mock 轻松替换掉,因为每个模块的功能都在协议中描述。当我们同时实现业务逻辑和相关测试时,我们已经在使用测试驱动开发(TDD)。由于每个逻辑案例的都是由协议定义的,因此可以先编写一个确定其行为的测试,然后直接实现该方法。

第三,Clean Swift 有单向的数据处理和决策流程(这是和 VIPER 相对的),始终只有一个循环 View Controller --> Interactor --> Presenter --> View Controller,这简化了重构。因为大部分的时候,将会修改更少的实体。因此,使用 Clean Swift 架构时,具有经常更改逻辑的项目更容易重构。在 Clean Swift 中,可以通过两种方式分离实体:

  • 通过在声明输入和输出协议中隔离组件
Clean Swift Isolation.png
  • 通过使用结构隔离功能,并将数据封装到单独的 Request/Response/ViewModel 中。每个功能都有其逻辑,并在同一过程中进行控制,不会与其他功能重叠。

Clean Swift 不应该用于没有长远眼光的小型项目,也不应该用于原型。相反,长期的项目和具有大量业务逻辑的项目非常适合这种架构。当项目为 Mac OS 和 iOS 两个平台开发(或者有此计划)时,使用 Clean Swift 非常方便,因为大部分代码模块(除 View Controller 外,有时也会包括 Router 在内)可以不用修改就被重用。

不当之处,还请指正

附:Clean Swift 官网

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容