App架构

iOS APP 架构本质探讨(一)

2018-11-09  本文已影响33人  Easyhoony

APP 架构是APP设计的一个 重要组成部分,就像框架结构的楼房一样, 决定了你 APP 随着版本迭代、业务量增加的延展性, 架构师一直在关心如何设计一个 app 的架构;

具体来说,它关注于两个方面 :

我们通常使用简单的框图来解释 app 的架构。比如,Apple 官方推荐的 MVC 模式可以通过 Model(模型)、View(UI) 和 Controller( 视图容器控制器) 三层结构来描述;

MVC 的相互关系

框图中的模块展示了MVC模式中不同名字的三个层次。在一个 MVC 项目中,绝大部分的代码都会落到其中某个层上(其实一般都会落在Controller 上)。箭头表示了这些层进行连接的方式。

这种简单的框图几乎无法解释在实践中模式的操作方式。这是因为在实际的 app 架构中,根据 APP 功能和业务可能会产生很多的模块儿(业务模块儿、功能模块儿)。事件流在各个层中通讯的方式是什么?模块儿之间是否应该在编译期间或者运行时持有(依赖)对方?要怎么读取和修改不同模块儿中的数据?以及状态的变更应该以哪条路径在 app 中穿行?

View 和 Model 的关系

在最高的层级上,APP 架构其实就是一套Extension,APP中不同的功能的小模块儿会被归纳到某个类型中去; 我们将这些不同的种类叫做层次:一个层次指的是,遵循一些基本规则并负责特定功能的接口和其他代码的集合。

Model 层和 View 层是这些分类中最为常见的两个.

 Model 层是 APP 的数据内容载体,它不依赖于apple 的任何框架; 也就是说, 程序员对 Model 层有完全的控制;  Model 层通常包括 Model 对象.

    View 层是依赖于 apple 框架的部分,它使 Model 层可见,并允许用户进行交互,从而将 model 层转变为一个 APP的数据载体。当创建 iOS 应用时,View 层几乎总是直接使用 UIKit;不过,我们也会看到在有些架构中,会使用 UIKit 的封装来实现不同的 View 层。另外,对一些其他的像是游戏这样的自定义应用,View 层可以不是 UIKit 或者 AppKit,它可能是 SceneKit 或者 OpenGL 的某种封装。

有时候,我们选择使用Struct或者enum来表示 Model 或者 View 的实例,而不使用类的对象。在实践中,类型之间的区别非常重要,但是当我们在 Model 层中谈到对象、结构体和枚举时,我们会将三者统一地称为 Model 对象。类似地,我们也会把 View 层的实例叫做 View 对象,实际上它们也可能是对象、结构体或者枚举.

View 对象通常会构成一个单一的 View 层级,在这个层级中,所有的 View 对象通过树结构的方式连接起来。在树的根部是Window,屏幕中存在若干窗口,接下来在树的分支和叶子上是更多的小 View。类似地,ViewController 也通常会形成 ViewController 层级. 不过,Model 对象却不需要有明确的层级关系,在程序中它们可以是互不关联的独立 Model.

当我们提到 View 时,通常指的是像一个按钮或者一个文本 UILabel 这样的单一 View 对象。当我们提到 Model 时,我们通常指的也是像一个 Recording 实例或者 Folder 实例这样的单个 Model 对象。在该话题的大多数文献中,"Model" 在不同上下文中指的可能是不同的事情。它可以指代一个 Model 层,Model 层中的具体的若干对象,文档 Model,或者是 Model 层中不关联的文档。虽然可能会显得啰嗦,我们还是会尝试在本书中尽量明确地区分这些不同含义。

为什么 Model 和 View 的分类会被认为是基础中的基础?

当然,就算不区分 Model 层和 View 层,写出一个 APP 也是绝对可能的。比如说,在一个简单的对话框中,通常就没有独立的 Model 数据。在用户点击 OK 按钮的时候,我们可以直接从用户界面元素中读取状态。不过通常情况下,Model 层的缺失,会让程序的行为缺乏对于清晰规则的依据,这会使得代码难以维护。

定义一个 Model 层的最重要的理由是,它为我们的程序提供一个表述事实的单一来源,这会让逻辑清晰,行为正确。这样一来,我们的程序便不会被应用框架中的实现细节所支配。

应用框架为我们提供了构建 APP 所需要的基础设施,我们使用 Cocoa - 或者更精确说,根据目标平台,使用 UIKit,AppKit 或者 WatchKit - 来作为应用框架。

如果 Model 层能做到和应用框架分离,我们就可以完全在 app 的范围之外使用它。我们可以很容易地在另外的测试套件中运行它,或者用一个完全不同的应用框架重写新的 View 层。这个 Model 层将能够用于 Android,macOS 或者 Windows 版本的 app 中。

APP 架构到底是在做什么

View 层和 Model 层需要交流。所以,两者之间需要存在连接。假设 View 层和 Model 层是被清晰地分开,而且不存在无法解耦的联结的话,两者之间的通讯就需要一些形式的翻译:

交互关系

从根本上说,用户界面是一个同时负责展示和输入功能的反馈设备,所以毫无疑问,这导致的结果就是一个反馈回路。每个 APP设计模式所面临的挑战是如何处理这张图表中箭头所包含的交流,依赖和变换。

在 Model 层和 View 层之间不同的路径拥有不同的名字。用户发起的事件会导致 view 的响应,我们把由此引起的代码路径称为 ViewAction,像是点击按钮或者选中 tableView 中的某一行就属于 viewAction。当一个 viewAction 被送到 Model 层时,它会被转变为modelAction (或者说,让 Model 对象执行一个 action 或者进行update的命令)。这种命令也被叫做一个Message(特别在当 model 是被 reducer 改变时,我们会这么称呼它)。将 viewAction 转变为 modelAction 的操作,以及路径上的其他逻辑被叫做交互逻辑。

译者注:reducer 这个名字来自于函数式编程的 reduce 操作,它指的是对一系列的值进行操作,把它们按照一定逻辑“减少” (合并) 到一个值中。一般来说,架构中的 reducer 所负责的事情是,将一系列未来到来的变化 (或者说消息),与当前的状态进行“合并”,并推导出新的状态。我们会在后面的章节,特别是 TEA 的相关内容中,看到一个实际的经典 reducer 的例子。
一个或者多个 model 对象上状态的改变叫做 model 变更。Model 的变更通常会触发一个 model 通知,比如说从 model 层发出一个可观测的通知,它描述 model 层中什么内容发生了改变。当 view 依赖于 model 数据时,通知会触发一个 view 变更,来更改 view 层中的内容。这些通知可以以多种形式存在:Foundation 中的 Notification,代理,回调,或者是其他机制,都是可以的。将 model 通知和数据转变为 view 更改的操作,以及路径上的其他逻辑被叫做表现逻辑。

根据 app 模式的不同,有些状态可能是在文档 model 之外进行维护的,这样一来,更新这些状态的行为就不会追随文档 model 的路径。在很多模式中的导航状态就这种行为的一个常见例子,在 view 层级中的某个部分 (或者按照 Cocoa Storyboard 中使用的术语,将它称为scene) 可能会被换出或者换入层级中。

在 app 中非文档 model 的状态被叫做 view state。在 Cocoa 里,大部分 view 对象都管理着它们自己的 view state,controller 对象则管理剩余的 view state。在 Cocoa view state 的框图中,通常会有加在反馈回路上的捷径,或者单个层自身进行循环。在有一些架构中,view state 不属于 controller 层,而是属于 model 层的部分 (不过,根据定义,view controller 并不是文档 model的一部分)。

当所有的状态都在 Model 层中被维护,而且所有的update都通过完整的反馈回路路径进行传递时,我们就将它称为单向数据流。当任意的 View 对象或者中间层对象只能够通过 Model 发出的通知来进行创建和更新 (View 或者中间层不能通过捷径来更新自身或者其他的 view) 时,这个模式通常就是单向的。

架构任务

Apple 平台的标准 Cocoa 框架提供了一些架构工具。Notification 将值从单一源广播给若干个收听者。键值观察 (KVO) 可以将某个对象上属性的改变报告给另一个对象。然而,Cocoa 中的架构工具十分有限,我们将会使用到一些额外的框架。

响应式编程也是一种用来交流变更的工具,不过和通知或者 KVO 不同的是,它专注于在源和目标之间进行变形,让逻辑可以在部件之间传输信息的同时得以表达。

我们可以使用像是响应式编程或者 KVO 这样的技术创建属性绑定。绑定接受一个源和一个目标,无论何时,只要源发生了变化,目标也将被更新。这和手动进行观察在语法上有着不同,我们不再需要写观察的逻辑,而只需要指定源和目标,接下来框架将会为我们处理其余部分的工作。

macOS 上的 Cocoa 包含有 Cocoa 绑定技术,它是一种双向绑定,所有的可观察对象同时也是观察者,在一个方向上建立绑定连接,会在反方向也创建一个连接。不论是 (在 MVVM-C 的章节中用到的) RxCocoa,还是 (MAVB 章节 中用到的) CwlViews,都不是双向绑定的。所以,在本书中,所有关于绑定的讨论都只涉及到单向绑定。

APP 任务

要让程序正常工作,View 必须依赖于 Model 数据来生成和存在,操作View,让它可以对 Model 进行更改,并且能在 Model 更新时也得到更新。

所以,我们需要决定在 app 中如何执行下列任务:

构建 — 谁负责构建 Model 和 View,以及将两者连接起来?
更新 Model — 如何处理 viewAction?
改变 View — 如何将 Model 的数据应用到 View 上去?
viewState — 如何处理导航和其他一些 modelState 以外的状态?
测试 — 为了达到一定程度的测试覆盖,要采取怎样的测试策略?
对于上面五个问题的回答,是构成 APP设计模式的基础要件。在后面的内容中,会逐一研究这些设计模式。

上一篇下一篇

猜你喜欢

热点阅读