美文网首页
Clean Swift

Clean Swift

作者: plantseeds | 来源:发表于2019-10-03 17:38 被阅读0次
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 官网

相关文章

网友评论

      本文标题:Clean Swift

      本文链接:https://www.haomeiwen.com/subject/nvrpyctx.html