MVC\MVP\MVVM设计模式笔记

2017-10-19  本文已影响0人  encoreMiao

关于设计模式演化(其实是不断增多的需求来推动的)

当需求变得庞大,解决方案也会变得庞大;当解决方案变得庞大,就会出现细分,出现细分,就会出现按哪种方式管理的问题。软件从处理一件事务发展到了要处理许多事物。各事务间有包含、顺序、主次等关系。变得越来越复杂。因为数据与逻辑庞大了,所以数据与逻辑就分离了。然后事件和业务分离了。
大致分为:界面、数据、事件、业务

针对iOS来说(why care about choosing the architecture)

如果不使用合适的软件架构,那么终会有一天,你需要调试一个非常庞大,包含了众多业务逻辑的类,那时你就会发现自己根本无从下手。通常来说,开发者无法记住一个庞大的类的所有业务逻辑,因此在分析过程中,往往会因为类的内容过多而忽略掉很多重要的细节。

怎样才是好的设计模式呢(what's a good architecture)

MV(X)

View: 界面
Model:数据
Controller/Presenter/Viewmodel:the glue or mediator between M and V.

MVC

M-V-C.png

MVC的流程大概如下:

  1. A View triggers an action to happen on the Controller
  2. The Controller executes said action and updates the Model
  3. The controller may receive a notification from the model that it was updated.
  4. The Controller updates the View

代码:View通知Controller所有的用户操作,之后Controller更新Model,Model一般会通过kvo来通知别的Controller来更新View上的展示。

对于Controller来说:

  1. managing the view hierarchy of the view they own.
  2. respond to the view loading, appearing, disappearing, and so on .
  3. model logic(Models)
  4. business logic(Views)
    其中V和VC之间的界限模糊,是紧紧绑在一起的,Controller要维护View的生命周期,要接受View的事件,要对View进行定制。另一方面,Controller会更新Model也要响应Model的通知。似乎看起来Controller会变的很重。但这不是最严重的问题,最严重的问题是这种情况下View和Controller都会变的很重,变的难以复用。

iOS architecture, where MVC stands for Massive View Controller.

  • 厚重的View Controller
    由于大量的代码被放进view controller,导致他们变的相当臃肿。在iOS中有的view controller里绵延成千上万行代码的事并不是前所未见的。这些超重app的突出情况包括:厚重的View Controller很难维护(由于其庞大的规模);包含几十个属性,使他们的状态难以管理;遵循许多协议(protocol),导致协议的响应代码和controller的逻辑代码混淆在一起。
    厚重的view controller很难测试,不管是手动测试或是使用单元测试,因为有太多可能的状态。将代码分解成更小的多个模块通常是件好事。(Massive view controllers are difficult to test, either manually or with unit tests, because they have so many possible states. )
  • 遗失的网络逻辑
    苹果使用的MVC的定义是这么说的:所有的对象都可以被归类为一个model,一个view,或是一个controller。就这些。那么把网络代码放哪里?和一个API通信的代码应该放在哪儿?
    你可能试着把它放在model对象里,但是也会很棘手,因为网络调用应该使用异步,这样如果一个网络请求比持有它的model生命周期更长,事情将变的复杂。显然也不应该把网络代码放在view里,因此只剩下controller了。这同样是个坏主意,因为这加剧了厚重View Controller的问题。
    那么应该放在那里呢?显然MVC的3大组件根本没有适合放这些代码的地方。
  • 较差的可测试性
    MVC的另一个大问题是,它不鼓励开发人员编写单元测试。由于view controller混合了视图处理逻辑和业务逻辑,分离这些成分的单元测试成了一个艰巨的任务。大多数人选择忽略这个任务,那就是不做任何测试。
  • 定义模糊的“Manage”
    之前我提到了view controller可以管理试图的层次结构;view controller有一个“view”属性,并且可以通过IBOutlet访问视图的任何子视图。当有很多outlet时这样做不易于扩展,在某种意义上,最好不要使用子视图控制器(child view controller)来帮助管理子视图(subview)。
    要点在哪?验证用户输入的业务逻辑应归入controller还是model呢?
    在这里有多个模糊的标准,似乎没有人能完全达成一致。貌似无论如何,view和对应的controller都紧紧的耦合在一起,总之,还是会把它们当成一个组件来对待。

在MVC中,VC更像是一个整体。也叫做M-VC。

Cocoa MVC is the pattern of your choice if you are not ready to invest more time in your architecture, and you feel that something with higher maintenance cost is an overkill for your tiny pet project.
Cocoa MVC is the best architectural pattern in terms of the speed of the development.(快速开发会用)

对于MVC来说,有一部分逻辑确实是属于 controller 的,但是也有一部分逻辑是不应该被放置在 controller 中的。比如,将 model 中的 NSDate 转换成 view 可以展示的 NSString 等。在 MVVM 中,我们将这些逻辑统称为展示逻辑

MVP

在 Apple 的 MVC 中,View 和 Controller 是紧密耦合的,但在 MVP 中,Presenter 与 View/View Controller 完全解耦,Presenter中没有任何与 View 布局相关的代码,View 可以很方便地进行移植。即便这样,Presenter 依旧肩负着对 View 的数据更新和动作捕捉。
耦合度 —— 在此架构中, Presenter 和 Model 模块的职责功能最分明,同时具备一个被尽量简化的
可测试性 —— 具备非常好的可测试性,我们可以通过 View 来测试大部分业务逻辑。
易用性 —— 对于我们这个没有实用性的简单例子来说,MVP 架构的代码量几乎是 MVC 的两倍,但同时,MVP 在思路上更加清晰。

MVVM

从图上看是比MVP简单了,更不用说MVC了。MVVM是一种软件架构模式,是MVC\MVP的演进,促进了UI代码和业务逻辑的分离(个人不认为MVVM是从MVP进化而来),我只觉得这是在MVP之后出现的一种“更好的”UI模式解决方案,但是用MVP来与之对比比较容易说明问题。
MVVM将表示逻辑从Controller里移除放到一个新对象里,即viewmodel。这样可以减少ViewController的复杂性,并使得表示逻辑更易于测试。ViewModel大致上就是MVP的Presenter和MVC的Controller了,而View和ViewModel间没有了MVP的界面接口,而是直接交互,用数据“绑定”的形式让数据更新的事件不需要开发人员手动去编写特殊用例,而是自动地双向同步。数据绑定你可以认为是Observer模式或者是Publish/Subscribe模式,原理都是为了用一种统一的集中的方式实现频繁需要被实现的数据更新问题。
比起MVP,MVVM不仅简化了业务与界面的依赖关系,还优化了数据频繁更新的解决方案,甚至可以说提供了一种有效的解决模式。

M-V-VM.png

优缺点:

分层模式

该图是推荐的viewmodel的实现方式,首先这是标准的mvvm的实现模式。
这里view model分为两部分,一部分是最基础的view model部分,只包含和的view的相关的一些属性,作为父类。所有的业务逻辑写在子类中,显然不同子类就可以实现不同业务线的业务逻辑。


优势

Under MVVM, the view and view controller become formally connected; we treat them as one. Views still don’t have references to the model, but neither do controllers. Instead, they reference the view model.
The one thing that does not belong in the view model is any reference to the view itself. The logic in the view model should be just as applicable on iOS as it is on OS X. (In other words, don’t #import UIKit.h in your view models and you’ll be fine.)

总结

在一个 app 中使用多种软件架构其实是很正常的。例如你的项目开始时使用的是 MVC ,后面你可能发现个别复杂的页面使用 MVC 架构实现时会变得难以维护,此时你可能会使用 MVVM 架构对该界面代码进行重构。但并不需要修改其他使用 MVC 架构的运行良好的页面代码。
对于MV(X)家族,都是在经典MVC基础上随着时代的发展、应用环境的变化衍变出来的。实现MV(X)模式的这些框架到底归属于哪种模式,也不必泥古。MV(X)是一个很有争议性的话题,能够构建一个健壮、具有良好设计、遵从关注点分离的项目比花时间去争论到底是MV(X)更有意义。

如何绑定?

举个例子:
Person:NSString *salutation
PersonViewController:一些判断以及格式化的逻辑 self.namelabel.text = self.model.salutation
加入Viewmodel后 (同样的代码只是移动了位置,便简化了VC,也方便了单元测试)
PersonViewModel: 一些判断以及格式化的逻辑
PersonViewController:只需要 self.namelabel.text = self.viewmodel.salutation

这个例子中的model是不可变的,所以只在初始化的时候指定我们的viewmodel的属性。对于可变model,我门还需要一些绑定机制,一旦viewmodel上的model发生改变,那么view的属性也需要更新,model的改变应该级联向下通过viewmodel进入view。

OSX : Cocoa
iOS :KVO (需要很多样板代码),ReactiveCocoa。
( • 基于 KVO 库设计的 RZDataBinding 和 SwiftBond

ReactiveCocoa(函数式响应式编程框架)

reactive programming

在 iOS 开发中,系统并没有提供类似的框架可以让我们方便地实现 binder 功能。而GitHub开源的RAC为我们提供了一个不错的选择。
在 iOS 的 MVVM 实现中,我们可以使用 RAC 来在 view 和 viewModel 之间充当 binder 的角色,优雅地实现两者之间的同步。此外,我们还可以把 RAC 用在 model 层,使用 Signal 来代表异步的数据获取操作,比如读取文件、访问数据库和网络请求等。说明,RAC 的后一个应用场景是与 MVVM 无关的,也就是说,我们同样可以在 MVC 的 model 层这么用。

架构相关:MVVM With ReactiveCocoa
Basic MVVM with ReactiveCocoa(重要)
iOS中MVC已死MVVM当立?
Model-View-ViewModel for iOS(en)
MVVM 介绍
KVO实现MVVM
iOS 软件架构 - MVC, MVP, MVVM 和 VIPER 「译」 原文(en)
被误解的MVC和被神化的MVVM
https://objccn.io/issue-15-5/
MVVM 与 FRP 编程实战(有视频)
书籍:《FunctionalReactiveProgrammingOniOS》
测试相关:
如何对ViewModel进行单元测试
XCTest Assertions 及其种类
iOS 单元测试
What's Worth Unit Testing in Objective-C?

上一篇 下一篇

猜你喜欢

热点阅读