如何通过学习旧的 iOS 架构来构建良好的 iOS 架构?
一些历史
1979 年, Trygve Reenskaug 提出了 MVC 解决用户与设备间交互的一般解决方案最初的论文引起了人们的极大兴趣, 最终, 许多公司和个人提出了自己对 MVC 思想的理解和实现, 他们并不局限于 ModelView 和 Controller 的原始定义
最初的 MVC
1979 年的论文中对 MVC 的描述:
Models
Models 代表认知 Model 可以是单个对象(相当乏味), 也可以是对象的一些结构
- Models represent knowledge. A model could be a single object (rather uninteresting), or it could be some structure of objects.
- Views
View 是 model 的可视化表示它通常 (可视化地) 展示 model 的某些属性并忽略其他属性
- A view is a (visual) representation of its model. It would ordinarily highlight certain attributes of the model and suppress others.
- Controllers
Controller 是用户和系统之间的连接控制 views 在屏幕上的排版, 让用户进行输入
A controller is a link between a user and the system. It provides the user with input by arranging for relevant views to present themselves in appropriate places on the screen.
PS:Controllers 最初的命名是 Editors, 后来更改了名称
到目前为止听起来很熟悉
MVC 中令人惊讶的事实:
Controllers
Controller 接收用户的输出, 将其转换为适当的消息, 并将这些消息传递给一个或多个 view
The controller receives the users output, translates it into the appropriate messages and passes these messages on to one or more of the views.
因此 Controller 知道并更新 Views
Controller 永远不应该补充 Views, 例如, 不应该将它与 Views 的节点用箭头连接起来
A controller should never supplement the views, it should for example never connect the views of nodes by drawing arrows between them.
所以 Controller 应该与视觉表现无关
Controller 连接到它的所有 Views, 它们被称为 Controller 的部分
A controller is connected to all its views, they are called the parts of the controller.
这意味着只要 Controller 创建了 View, 并且不是被注入的, 那么它就可能知道 Models
... 这个 Editor 与前一个 (Editor) 非常相似, 但它是在网络创建的网络及其所有活动可以直接通过输入 doit 命令并执行
This Editor is very similar to the previous one, but it has been created in the environment of a demonstration network. Messages to that network and all its activities may therefore be typed in and executed directly through the doit command.
如果我们回忆起 Editor 是 Controller 的旧名称, 并考虑到网络和活动是 Models, 我们可以得出这样的结论: Controller 确实知道并更新 Models
Views
View 被附加到 Model(或 Model 部分), 并通过提问来获得展示 Model 所需的数据它还可以通过发送适当的消息来更新 Model
A view is attached to its model (or model part) and gets the data necessary for the presentation of the model by asking questions. It may also update the model by sending appropriate Messages.
所以 Views 知道并更新 Models.
Surprise!
让我们把拼图的所有部分拼在一起:
看起怎么样?
最初的 MVC 有两个重大的问题:
它破坏了一些好的软件设计规范
在 iOS 中实现是不切实际的
对 MVC 的改造
单一职责
Views 和 Controllers 都负责更新模型, 但我们希望只有一个实体有这样的职责这就是为什么大家提出单向数据流:
松耦合与高内聚
Views 知道 ModelsControllers 知道 Models 和 Views 这样的话, Models 就会依赖于 Views 和 Controllers 这意味着, 当我们修改 Model 的接口时, 我们不得不修改 Controllers 和 Models
但理想情况下, 我们希望最小化受我们的更改影响的实体的数量, 因此我们希望最小化它们间的依赖
根据迪米特法则(Law of Demeter, 又称最少知道原则), 我们希望一个角色对其他角色尽可能少的了解, 它们之间通过友元类进行交互, 而不是直接交互
在这个基础上, 我妈可以删除 View 更改 Model 的能力, 让 Controller 来负责这个操作
不和陌生人说话
最初的 MVC 是不切实际的尤其是在 iOS
最初的 MVC 允许 View 和 Controller 的角色由同一对象实现:
在简单的情况下, ModelView 和 Controller 角色可以由同一对象实现
In simple cases, the Model, View, and Controller roles may be played by the same object.
UIViewController 完全承担了所有角色的谈话者的职责所以说, 苹果的 MVC 似乎遵循了最初的 MVC 思想, 除了输入大多来自 UIControlls(属于 Views)而不是 Controllers
如果我们比较一下, 它们看起来一样:
最初的 MVC
苹果的 MVC
一个对象有两个职责导致我们将大多数业务逻辑保存在一个对象中如果不及时发现, 这种方法可能最终出现臃肿的 ViewControllers 虽然有一个独立的 Model 实体, 但是开发人员通常不知道 Models 是业务逻辑的主要维护者, 并且不利用 Model 将其他实体完全分离
我不确定你是不是在浪费我时间, 还是我真的能学到什么
从 MVC 中学习
如果我们把 MVC 看作是一个模式, 而不是一个良好的架构指南, 它就变得非常有用
事实上, MVC 中有很多东西是正确的
关注点的分离
MVC 定义了三个角色或职责, 这是解决用户与设备或应用程序交互的问题的一个大概括如果我们在抽象层面看而不是谈论实现细节, Model 和 View 的职责非常清楚同时, Controller 的责任是值得商榷的, 似乎是从处理用户输入的 Model 和 View 之间的中介
Facade(外观模式)
MVC 使用一个 Model 来表示用户对真实对象或现象的心理模型 Model 包含了数据和如何更改数据的逻辑这种逻辑通常称为业务逻辑人们往往忽略了逻辑部分, 只把 Models 的数据变成对象, 然后由 Controllers 操纵所有的逻辑, 导致 Models 消瘦, Controllers 臃肿
适当的 Models 是对象和数据的集合, 它们实际上是整个应用程序的真实来源
Facade 是一个隐藏其他物体群的物体, 就像建筑物的正面隐藏着里面的许多房间一样当应用外观模式 时, 这些对象的交互完全是通过 Facade 对象来完成的, 就像人们从建筑物正面的入口进出建筑物一样我们避免直接访问隐藏的对象, 就像我们避免通过窗口跳跃以更快地进入房间一样
Facade 举例
外观模式确保了 Models 的可伸缩性当一些 Model 对象增长时, 将大量的对象传递给 Controllers, 或者当你注意到 Controllers 中大量的业务逻辑变得不方便时, 考虑将这些 Model 对象封装在一个 Facade 中, 这样就隐藏了复杂性和实现细节
Facade 阻止业务逻辑离开 Model 层并移动到 Controller 层
Model 层的 Facade
Observer(观察者模式)
从 Models 中移除依赖项的技术之一是观察者模式顶层的想法是, 一个 Model 不知道谁使用它, 只是通过通知或回调通知潜在用户某事的发生或数据发生了变化
理解观察者模式最简单的方法是想象一个对象, 它利用无线电站传播变化信号, 其他对象使用收音机收听信号
Observer 示例
当您将 Model 层构建为独立的服务 时, 这种技术就变得特别强大
服务是有状态对象, 其生命周期通常与应用程序的生命周期相等例如网络服务特征服务分析服务聊天服务
Controllers 可以使用简单的代码来获取到服务的状态和更改
Model 层的 Observers
Mediator(中介者模式)
我们来看看 Controllers 所扮演的角色: 有了 Models, 也有了 Views 我们还需要 Controllers 吗? 没有 Controllers 将会导致我们直接从 Views 访问 Models, 这将违反单一责任原则
此外, 我们很难编写测试用例, 因为 Views 依赖于平台(UIKit 等), 每当你想测试 Views 中的代码时你都要考虑处理 Views 在 UIViewController 中的生命周期
相反, 我们在 Models 和 Views 之间设置 Controllers 作为中介它们非常瘦, 比 Models 和 Views 轻量, 因为它们知道 Models 和 Views, 因此它们比 Models 或 Views 具有更多依赖当然, 对象的依赖性越强, 你要放进去的逻辑就应当越少承担较少的责任将减少复杂性, 从而减少出错的机会
因此, Controllers 就成为在 Models 和 Views 之间一个很轻但有时又脏的胶水(中介)
Mediator 示例
由于我们的 Model 层通常是有状态的, 我们应该尽可能的避免 Controllers 存储状态, 而是访问存储在 Model 层的数据然后进行计算
但这并总是可行的如果要实现业务逻辑所需的某些记帐方式很难放入相应的服务 / 模型中, 那么我们应该选择它们中弊端较小的做法, 并在 Controller 中处理极端情况
MVC 的发展
让我们看看现有的架构中是如何分配 MVC 的角色的
请确保你已经读过 iOS 架构设计 这篇文章, 以便我们能够相互理解
苹果的 MVC
Realistic Cocoa MVC + MVC Roles
显然, View 和 Controller 的角色由 UIViewController 担当
MVP/MVVM
MVP + MVC Roles
MVVM + MVC Roles
满足了单一职责原则, 每个实体都有相应的作用
VIPER/Riblets.
VIPER + Service + MVC Roles
原本的 VIPER/Riblets 设计图并没有提到它必须有一个 服务 / 存储 / 存储(Service/Repository/Storage) 将 Entities 和 Interactor 关联起来作为 Model 层(我已经添加到上面的图)
我们至少有两个 Controllers: 一个 Presenter(提交者)和一个 Router(路由器) Presenter 处理 UI 相关的逻辑, 而 Router 负责模块之间的通信
总结
不要试图将 MVC 作为一种单一的架构设计模式, 而是作为良好的应用程序体系结构的指导方针, 或者一组可以解决我们一些问题的设计模式
确保你不仅能够在面试中解释一种模式, 而且要理解这种模式是如何解决作为程序员的日常生活中的实际问题的
在开发过程中适当地设计模式 (提升代码质量) 牢记 YAGNI(you aint gonna need it, 你不需要它)的原则, 而不是选择一个架构, 然后把你的应用程序硬塞到它的框架中
避免盲目的将框架 (比如说 VIPER/Riblets) 照搬到实际的项目中, 需要考虑到实际业务的增长需求死板的代码会产生成吨的冗余记住, 我们总是可以在多个实体之间划分角色, 但是如果没有必要, 就避免这样做
设计模版的目的是使用户能够获取信息或与系统交互, 而不是让开发人员偷懒同时, 避免生产最终代码 (明天不是世界末日) 你也许会把今天的代码作为明天开发新功能的基础
来源: https://www.thinksaas.cn/group/topic/838897/