这篇博客的目的是为了介绍Model-View-ViewModel(MVVM)模式。过去,我参加过很多关于MVVM模式的在线讨论,我发现MVVM的初学者为了在自己的代码中实现该模式,要么没有多少资料参考,要么就在费力地研究一些自相矛盾的资料。我不是要制定MVVM的规范,只是想把关键概念罗列到一篇博客中,以使读者能够更容易,更直接地理解MVVM的核心内容,实现方法。MVVM比人们想象的容易的多。
为什么需要MVVM模式?
作为一个开发者,为什么你应该关注Model-View-ViewModel(MVVM)模式?引入MVVM有很多的好处,在这之前,先尝试着问自己一些问题:
(1)你需要和设计师协作工作吗?你需要让几乎同时进行的设计工作和开发工作具有更高的灵活性吗?
(2)你的解决方案需要进行单元测试吗?
(3)在项目、甚至是公司里面重用一些公共组件对你来说是重要的吗?
(4)在不修改代码块中的业务逻辑的前提下,你想要更灵活地修改用户界面元素吗?
如果对于上述问题中的任一个,你的答案是%26ldquo;Yes!%26rdquo;,那么采用MVVM模式能够给你的项目带来一点帮助。
我对网上的一些言谈感到很吃惊,例如%26ldquo;MVVM只对非常复杂的界面设计有意义!%26rdquo;,%26ldquo;对于小规模的应用程序,MVVM总会增加很多额外的开销。%26rdquo;,更好笑的说法是%26ldquo;MVVM是不可扩展的。%26rdquo; 就我而言,上面的说法说的是MVVM的实现,而不是MVVM自身。换句话说,如果你需要花费数小时理解MVVM,你的做法就是错的。如果你的应用程序扩展性差,别怪MVVM,要怪你自己是如何使用MVVM的。不管你采用何种设计模式,向ListBox绑定100000项列表项的做法都是愚蠢的。
当然,这只是我知道的MVVM,不是被广泛认可的MVVM。
Model-View-ViewModel(MVVM)模式,分为三层,分别是View层,ViewModel层,Model层,下面进行具体的介绍。
Model层
我把Model类比为领域对象。Model负责保存数据,管理状态。一个Model的例子是通讯录,每个联系人的信息包括姓名,电话,地址等等。Model的关键概念是保存数据,但是不操作数据。Model不负责将将文本格式化供屏幕显示来获得好的用户体验,也不负责从远程服务器上读取列表的选项数据。(事实上,列表中的每一个选项都更像是自己的Model。)业务逻辑通常是与Model分离开来的,被封装到与Model进行交互的其他类中,但也不是一种定律,有些情况下,Model中也会包括验证服务。建立纯粹的Model是一种挑战,我说的是一种代表%26ldquo;真实世界%26rdquo;的Model。例如,一条通讯记录可能包含最近修改日期,联系人的身份信息,和一个唯一的标识符(在数据库中存储的关键字诸如此类的信息)。在真实世界中,最近修改日期对通讯录是没有意义的,仅仅是作为一项功能在系统中跟踪Model是如何使用的。
View层
View层是我们大多数人熟悉的部分,同时也是用户唯一能够和系统交互的部分。View层负责显示数据。View能够在显示数据之前对其进行格式化,例如,存在在数据库中的日期可能是自1970年以来的毫米值,但是对于终端用户来说,他们看到的是当地时区年,月,日。View也能够接受来自用户的输入。View管理各种类型的输入,如键盘,鼠标,手势等,这最终将使用Model中的属性值。
在MVVM中,View是主动的。和与Model无关联的,完全由Controller和Presenter操作的被动View相反,MVVM中的View包含着交互,事件,数据绑定等操作,这最终将会关联到底层的Model和ViewModel。当这些事件和交互被映射到属性,方法调用,命令的时候,View会继续负责处理自己的事件,而不会将所有的操作委托给ViewModel。另外,需要注意的是View不再保持自己的状态信息,而是选择与ViewModel的数据同步。
ViewModel层
ViewModel是三层中的关键部分,因为它引入了表示分离的概念。相较于View直接使用Model中的原始数据,ViewModel首先将数据格式化,从而使Model只保存原始数据,View只显示格式化数据。ViewModel作为控制器,充当两者之间的联络员,ViewModel可能会接收来自View的输入,然后更新Model中的数据,或者通过接口与Model交互,然后翻译属性值以在View上显示。
ViewModel也会提供一些接口,命令或其他的接入点来帮助View保存状态,基于View上的交互结果操作Model,和触发View自身的事件。
View和ViewModel之间的交互
(1)View和ViewModel之间通过数据绑定,方法调用,属性值,事件,消息进行交互;
(2)ViewModel提供的接口调用不但包括来自Model的数据,还有属性值(如,状态信息,%26ldquo;isBusy%26rdquo;标识符),命令等。
(3)View处理自身的事件,然后通过命令将其映射到ViewModel;
(4)Model层的数据和ViewModel层的属性值通过双向的数据绑定更新数据;
ViewModel和Model之间的交互
(1)ViewModel可能直接使用Model,或者将属性值与Model相关联。
(2)为了操作供View调用的属性值,ViewModel可能包含一些服务接口,配置参数。
先有鸡还是先有蛋的问题?
你可能会听过关于先有View还是先有ViewModel的讨论?总的来说,我相信大多数开发者同意每一个View都有一个唯一与之对应的ViewModel,没必要为同一个View绑定多个ViewModel。如果你考虑过分离的概念,这就很有意义。一个View可能由许多其他View构成,每一个View都有自己的ViewModel。ViewModel有时也可能包含多个其他的ViewModel。虽然一个View应该只有一个ViewModel,但是一个ViewModel能够被多个View共享。
关于上述问题的答案,一种观点是先有View,这意味着View驱动ViewModel开发。在这种场景中,View通常作为一种资源,利用依赖注入与ViewModel绑定。另一种先有ViewModel的观点意味着ViewModel负责创建View,同时将自身绑定到View。
总结
MVVM不是一个完整的框架,它只是一种设计模式。MVVM不处理,事实上也不关注你的服务器上发生了什么,或者你的服务是怎么组织到一起的。MVVM只是强调一种表示分离的概念。
%26nbsp;
到此,对技术文摘Model-View-ViewModel (MVVM) Explained的翻译结束,希望能够增加读者对MVVM模式的认识。
%26nbsp;
参考文献:
%26nbsp;
[1] 英文原文:Model-View-ViewModel (MVVM) Explained。
%26nbsp;
本文历史:
%26nbsp;
- 2015-08-05 初稿完成。
%26nbsp;