如何通过学习旧的iOS架构来构建良好的iOS架构?
一些历史
1979年,Trygve Reenskaug提出了MVC ——解决用户与设备间交互的一般解决方案。最初的论文引起了人们的极大兴趣,最终,许多公司和个人提出了自己对MVC思想的理解和实现,他们并不局限于Model、View和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 user’s 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知道Models。Controllers知道Models和Views。这样的话,Models就会依赖于Views和Controllers。这意味着,当我们修改Model的接口时,我们不得不修改Controllers和Models。
但理想情况下,我们希望最小化受我们的更改影响的实体的数量,因此我们希望最小化它们间的依赖。
根据迪米特法则(Law of Demeter,又称最少知道原则),我们希望一个角色对其他角色尽可能少的了解,它们之间通过友元类进行交互,而不是直接交互。
在这个基础上,我妈可以删除View更改Model的能力,让Controller来负责这个操作。
不和陌生人说话
最初的MVC是不切实际的——尤其是在iOS
最初的MVC允许View和Controller的角色由同一对象实现:
在简单的情况下,Model、View和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 ain’t gonna need it,你不需要它)的原则,而不是选择一个架构,然后把你的应用程序硬塞到它的框架中。
避免盲目的将框架(比如说VIPER/Riblets)照搬到实际的项目中,需要考虑到实际业务的增长需求。死板的代码会产生成吨的冗余。记住,我们总是可以在多个实体之间划分角色,但是如果没有必要,就避免这样做。
设计模版的目的是使用户能够获取信息或与系统交互,而不是让开发人员偷懒。同时,避免生产“最终代码”(明天不是世界末日)。你也许会把今天的代码作为明天开发新功能的基础。
作者:忠橙_g
链接:https://www.jianshu.com/p/1006c3a62455
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。