界面之下:还原真实的MVC、MVP、MVVM模式

原文地址:https://linux.cn/article-6481-1.html
希望有人能懂,把mvc文章放设计模式分类下的用意。


前言

做客户端开发、前端开发对MVC、MVP、MVVM这些名词不了解也会大致听过,都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式。

网上很多文章都是关于这方面的讨论比较杂乱,各种MV*模式之前的区别不轻,甚至有些描述都是错误的。

本文追根溯源,从最经典的Smalltalk-80 MVC模式开始逐步还原图形界面之下最真实的MV*模式。

GUI程序所面临的问题

图形界面的应用程序提供给用户可视化的界面操作,这个界面提供给数据和信息。用户输入行为(键盘、鼠标等)会执行一些应用逻辑,

应用逻辑可能会触发一定的业务逻辑对应用程序数据的变更,数据的变更自然需要用户界面的同步变更以提供最准确的信息。 例如用户对一个电子表格重新排序的操作,应用程序需要相应用户操作,对数据进行排序,然后需要同步界面上。

在开发应用程序的时候,以求更好的管理应用程序的复杂性,基于职责分离的思想都会对应用进行分层。

在开发图形界面应用程序的时候,会把管理用户界面的层次称为View,应用程序的数据为Model(注意:助理的Model指的是Domain Model,这个应用程序对需要解决的问题的数据抽象,不包含应用的状态,可以简单理解为对象)。Model提供数据操作的接口,执行相应的业务逻辑。

有了View和Model的分层,那么问题就来了:View如果同步Model的变更,View和Model之间如何粘合在一起。

带着这个问题开始探索MV*模式,会发现这些模式之前的差异可以归纳为对这个问题处理的方式的不同。而几乎所有的MV*模式都是经典的Smalltalk-80 MVC的修订版。

Smalltalk-80 MVC

历史背景

早在上个世纪70年代,美国的施乐公司(Xerox)的工程师研发了Smalltakl编程语言,并且开始用它编写图形界面的应用程序。而在Smalltalk-80这个版本的时候,以为叫Trygve Reenskaug的工程师设计了MVC图形应用程序的架构模式,极大的降低了图形应用程序的管理难度。

而在四人帮(GoF)的设计模式当中并没有把MVC当做是设计模式,而仅仅把它看做解决问题的一类问题的集合。

Smalltalk-80 MVC和GoF描述的MVC是最经典的MVC模式。

MVC的依赖关系

MVC除了把应用程序分成View、Model层,还额外的增加了一个Controller层,它的职责为进行Model和View之间的协作(路由、输入预处理)的应用逻辑;Model进行处理业务逻辑。Model、View、Controller三个层次的依赖关系如下:

image

Controller和View都依赖Model层,Controller和View层可以相互依赖。在一些网上的资料Controller和View之前的依赖关系可能不一样,有些是单向依赖,有些是双向依赖,这个其实关系不大,后面会看到他们的依赖关系都是为了把处理用户行为触发的事件处理权交给Controller。

MVC 的调用关系

用户的对View操作之后、View捕获到这个操作,会把处理的权利交移给Controller;Controller会对来自View的数据进行预处理、决定调用Model的接口;然后由Model执行相关的业务逻辑;当Model变更了以后,会通过观察者模式通知View;View通过观察者模式收到Model变更的消息以后,会向Model请求最新的数据,然后重新更新界面。如下图:

image

看似没有什么特别的地方,但是有几个需要特别关注的关键点:

1、View是把控制权交移给Controller,Controller执行应用程序相关的应用逻辑(对来自View数据进行预处理,决定调用哪个Model的接口等)。
2、Controller操作Model,对Model执行业务逻辑对数据进行处理。但不会直接操作View,可以说它是对View无知的。
3、View和Model的同步消息是通过观察者模式进行,而同步操作是由View自己请求Model的数据然后对其视图进行更新。

需要特别注意的是MVC模式的精髓在于第三点;Model的更新是通过观察者模式告知View的,具体表现形式可以是Pub/Sub或者触发Events。而网上很多对于MVC的描述都没有强调这一点。通过观察者模式的好处就是:不同的MVC三角关系可能会有共同的Model,一个MVC三角中的Controller操作了Model以后,两个MVC三角的View都会接收到通知,然后更新自己。保持了依赖同一块Model的不同View显示数据的实时性和准确性。

我们每天都在用的观察者模式,在几十年前就已经被大神们整合到MVC架构当中。

MVC 的优缺点

优点:

1、把业务逻辑和展示逻辑分离,模块化程度高。且当应用逻辑需要变更的时候,不需要变更业务和展示逻辑,只需要Controller换成另外一个Controller就行了。
2、观察者模式可以做到多数图同时更新。

缺点:

1、Controller测试困难,因为视图同步操作是由View自己执行,而View只能在有UI的环境下运行。在没有UI环境下对Controller进行单元测试的时候,应用逻辑正确性是无法验证的:Model更新的时候,无法对View的更新操作进行断言。
2、View无法组件化。View是强依赖特定的Model的,如果需要把这个View抽出来作为另外一个应用的可复用组件就困难了。因为不同程序的Domain Model是不一样的。

MVC Model 2

在Web服务端开发的时候也会接触到MVC模式,而这种MVC模式不能严格成为MVC模式。经典的MVC模式只是解决客户端图形界面应用程序的问题,而对服务端无效。

服务端的MVC模式有自己特定的名字:MVC Model2,或者叫做JSP Model2。Model2客户端服务端的交互模式如下:

服务端接收到来自客户端的请求,服务端通过路由规则把这个请求交由特定的Controller进行处理,Controller执行相应的应用逻辑,对Model进行操作,Model执行业务逻辑以后;然后用数据去渲染特定的模板,返回给客户端。

因为HTTP协议是单工协议并且是无状态的,服务端无法直接给客户端推送数据。除非客户端再次发起请求,否则服务端的Model的变更就无法告知客户端。所以看到经典的Smalltalk-80 MVC中的Model通过观察者模式告知View更新这一环被无情的打破,不能成为严格的MVC。

Model 2模式最早在1998年应用在JSP应用程序当中,JSP Model 1应用管理的混乱诱发了JSP参考了客户端MVC模式,催生了Model 2.

后来这种模式几乎被应用在所有语言的Web开发框架中。PHP的ThinkPHP,Python的Dijango、Flask,NodeJS的Experss,Ruby的ROR,基本上都采用了这种模式。

平常讲的MVC基本上是这种服务端的MVC。

MVP

MVP模式有两种:
1、Passive View
2、Supervising Controller

而大多数情况下讨论的都是Passive View模式。本文会对PV模式进行较为详细的介绍,而SC模式则简单提及。

历史背景

MVP模式是MVC模式的改良。在上个世纪90年代,IBM旗下的子公司Taligent在用C/C++开发一个叫CommonPoint的图形界面应用系统提出来的。

MVP(Passive View)的依赖关系

MVP模式把MVC模式中的Controller换成了Presenter。MVP层次之前的依赖关系如下:

MVP 打破了原来View对于Model的依赖,其余的依赖关系和MVC模式一致。

MVP(Passive View)的依赖关系

既然View对Model的依赖被打破了,那View如何同步Model的变更?看看MVP的调用关系:

和MVC模式一样,用户对于View的操作都会从View交移给Presenter。

Presenter会执行相应的应用程序逻辑,并且对Model进行相应的操作;而这时候Model执行完业务逻辑以后,也是通过观察者模式把自己变更的消息传递出去,但是传给Presenter而不是View。Presenter获取到Model变更的消息以后,通过View提供的接口更新界面

关键点:
1、View不再负责同步的逻辑,而是由Presenter负责。Presenter中既有应用程序逻辑也有同步逻辑。
2、View需要提供操作界面的接口给Presenter进行调用。(关键

对比在MVC中,Controller是不能操作View的,View也没有提供相应的接口;而在MVP当中,Presenter可以操作View,View需要提供一组对界面操作的借口给Presenter进行调用;Model仍然通过事件广播自己的变更,但由Presenter监听而不是View。

MVP (Passive Sive)的优缺点

优点:

1、便于测试。Persenter对View通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现View的接口即可。然后通过依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用程序的正确性。
2、View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口给上层操作。这样就可以高度可复用的View组件。

缺点:
1、Presenter中除了应用逻辑之外,还有大量的View-> Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。

MVP(SuperVising Controller)

上面讲到的MVP的Passive View模式,该模式下的View非常Passive,它几乎什么都不知道,Presenter让它干什么就干什么。

而Supervising Controller模式中,Presenter会把一部分简单的同步逻辑交给View自己去做,Presenter之负责比较复杂的、高层次的UI操作,所以可以把它看成一个Supervising Controller。

Supervising Controller模式下的依赖和调用关系:


因为Supervising Controller用得比较少,讨论就到这里。

MVVM

MVVM可以看做可以一宗特殊的MVP(Passive View)模式,或者说对MVP模式的一种改良。

历史背景

MVVM模式最早是微软公司提出,并且大量使用在.NET的WPF和Sliverlight中。2005年微软工程师John Gossman在自己的博客中首次公布了MVVM模式。

ViewModel

MVVM代表的是Model-View-ViewModel,这里需要解释一下什么是ViewModel。ViewModel的含义就是“Model of View”,视图的模型。

它的含义包含了领域模型视图状态

在图形界面应用程序当中,界面所提供的信息可能不仅仅包含应用程序的领域模型,还可能包含一些领域模型不包含的视图状态,例如电子表格程序上需要显示当前排序状态是顺序还是逆序的,而这是Domain Model不包含的,但是也是需要显示的信息。

可以简单把ViewModel理解为页面上所显示内容的数据抽象,和Domain Model不一样,VIewModel更适合用来描述View。

MVVM的依赖

MVVM的依赖关系和MVP依赖基本一致,只不过是把P换成了VM。

MVVM的调用关系

MVVM的调用关系和MVP一样。但是在ViewModel当中会有一个叫Binder,或者是Data-binding engine的东西。

以前全部由Presenter负责的View和Model之前的数据同步交由给Binder处理。你只需要在View的模板语法当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。

当ViewModel对进行Model更新的时候,Binder会自定把数据更新到View上去,当用户对View进行操作,Binder也会自动把数据更新到Model上去。这种方式称为Two-Way data-binding,双向数据绑定。可以简单而不恰当地理解为一个模板引擎,但是会根据数据变更实时渲染。

也就是说,MVVM把View和Model的同步逻辑自动化了。

以前Presenter负责的View和Model同步不再手动地进行操作,而交由框架所提供的Binder进行负责。只需要告诉Binder,View显示的数据对应的是Model哪一部分即可。

MVVM的优缺点

优点:
1、提高可维护性。解决了MVP大量的手动同步问题,提供双向绑定机制。提高代码可维护性。
2、可测试性。以为同步逻辑交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大较少了对View同步更新的测试。
缺点:
1、过于简单的图形界面不适用,杀鸡不需用牛刀。
2、对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。
3、数据绑定的声明是指令式地写在View的模板当中的。这些内容是没办法去打断点Debug的。

结语

可以看到,从MVC->MVP->MVVM,就像一个打怪升级的过程,后者解决了前者遗留的问题,把前者缺点优化成优点。

同样的工程文件,代码从最开始的一堆文件,优化成了最后只需要20几行代码就完成。MV*模式之前的区分还是蛮清晰的,希望可以给这些模式理解比较模糊的同学带来一些参考和思路。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容