MVVM模式中最重要的一个原则

不要再ViewModel中引用View!

可以说只有理解了这一点,才算理解了MVVM。为什么要有Databinding,ICommand,为什么要有IInteractionRequest,INavigationAware,为什么要费这么大的劲搞这些东西,就是为了保证ViewModel对View的无知。

首先我们要明白,前端的MVVM,MVC都是RIA的专有领地。如果只是呈现内容的简单页面,是没有必要搞这些的。后来页面越来越复杂,用户可以进行的操作越来越多,我们就有了codebehind。silverlight中的codebehind和ViewModel有什么本质上的分别?为什么仅仅有codebehind是不够的?最简单来说codebehind是和视觉元素一体的,他们实际上是在一个类中,有着共同的生命周期和组织结构:如果你在codebehind中放了一个数据集,回头你想用到它,你只能通过visual tree先找到对象,再从字段中读取数据;如果数据你想在后台保存,你可能不得不同时保存它的视觉元素。如果你要访问其它元素所保存的数据,不得不反复地通过parent或是children来访问visual tree,你的代码中可能充满了parent.parent.parent这样的访问方式,如果你的程序存在着多个页面,需要进行页面跳转,如果最上层的元素想和最下层的元素通信,那事情就更糟糕了。

这个时候,ViewModel就派上用场了。

ViewModel的定位是:界面逻辑的抽象,它不用存放业务逻辑,那是Model的事情,也不用管页面渲染的事,那是View的事,它的工作,是对用户操作的简单抽象。比方说一个用户注册的界面,RegisterCommand就是一个抽象出的行为,用户可以通过注册按钮或是Enter键来触发这个行为——具体怎么做界面自便,ViewModel不关心界面是什么样的。

可是codebehind也可以做这个事情啊,是的codebehind也可以共用EventHandler,可是一旦视觉元素销毁了,EventHandler也就没有了。如果界面存在跳转,可能需要反复地注册与取消注册事件,实践中,事件重复注册和事件未取消注册是拖慢Silverlight程序的常见原因。

而ViewModel就不同了,由于ViewMode本身简单类即可,我们可以在上面进行各种各样地操作。指定一个基类是最基本的,你可以在基类中定义各种有用的操作;使用依赖注入来解耦,当然codebehind也可以注入,但和视觉元素元素捆绑注定了这不是个好主意——如果只是需要Text的值,又何必把TextBox也创建出来呢?通过IOC容器的生命周期管理来保存用户的临时数据,即使界面销毁了状态也能够重建;事件中心,彻底摆脱树遍历的烦恼——总之,能做的太多了。

那么,ViewModel中为什么不能有到View的引用呢?

说到了ViewModel是对界面逻辑的抽象,一旦和具体的View挂上钩,这种抽象就消失了。每当你调用一次View的方法,你的ViewModel就向codebehind退化一分。如果你大量调用View的方法,还不如写成codebehind,起码codebehind没有这种情况下出现的一个恶魔,那就是环引用,环引用味道很坏,看到了就要立即干掉。

ViewModel引用View还有一些坏处,比如不好测试,白盒测试只要和界面沾上边就超级麻烦,可以说哪种平台哪种技术都没有好办法;又比如ViewModel失去了适配多种View的能力,不过这种情况倒不是太常见。

 

Technorati 标记: Prism,MVVM
posted @ 2014-03-19 20:15  Narcissu5  阅读(428)  评论(0编辑  收藏  举报