表现层持续解耦带来的模式转变 MVC MVP MVVM

---微软WPF带来的团队变化和软件技术变化

 Model-View-ViewModel是一种架构模式,主要在WPF、Silverlight和WP7开发里使用,它的目标是从视图层移除几乎所有代码隐藏(code-behind)。交互设计师可以专注于使用XAML表达用户体验需求,然后创建和视图模型的绑定,而视图模型则是由应用程序开发者开发和维护的。

      MVVM是更加通用的Presentation模式的一个具体实现。MVVM视图模型包含概念模型而不是数据模型,所有业务逻辑和其它操作都是在模型和视图模型里完成的。

      MVVM 把应用程序的状态和行为进行封装隔离了用户界面和用户体验部分,从而使得设计者与开发者可独立工作,并易于协作。开发者可迅速的投入代码开发中,只关注应用逻辑即可。如果实现一些相对较小,后期变化不大的应用程序,可能并不关注设计模式,使用设计模式反而带来复杂度。但ViewModel却具备良好的伸缩性,在小型应用中也可带来不菲的好处。它还易于在最终的应用程序中利用开始构建的原型系统,使得异步编程变得简单等等。

      在MVP模式中,为了让UI层能够从逻辑层上分离下来,设计师们在UI层与逻辑层之间加了一层interface。无论是UI开发人员还是数据开发人员,都要尊重这个契约、按照它进行设计和开发。这样,理想状态下无论是Web UI还是Window UI就都可以使用同一套数据逻辑了。

 

  与用户界面相关的最大的问题就是大量的凌乱的代码,

    a) 用户界面包含负责的逻辑用于维护界面相关对象;

    b) 包含了应用程序状态的维护;

  表现模式 (Presentation patterns) 就是围绕如何移除用户界面的复杂性,让界面更加简洁和可管理而产生的,下图就是常见表现模式的种类与分类: 

(1) MVC:模型-视图-控制器(Model View Controller),它强制性的使应用程序的输入、处理和输出分开。

(2) MVP:模型-视图-表现类(Model-View-Presenter)

(3) MVVM:模型-视图-视图模型(Model-View-ViewModel)

 

 

 

       (a) 发展过程:MVC->MVP->MVVM 

       (b)  MVC->MVP

       (c)  MVC、MVP->MVVM 

用户界面的3大问题:状态 (State) , 逻辑 (Logic) ,同步 (Synchronization)

  • 状态 (State) : 状态是用户界面最关心的问题之一。状态是用户界面数据的当前快照,在 Web 应用中,可能是 Session 级别的一个变量,在 Windows 应用中, 则可能只是界面级别的数据。 用户界面包含的状态越多, 则用户界面越复杂。
  • 逻辑 (Logic) : 用户界面往往包含界面逻辑,例如维护文本框、组合框或者其它任何界面元素,用户界面中这种逻辑越多,则用户界面越复杂。
  • 同步 (Synchronization) : 用户界面通常需要和业务组件协作,因此用户界面需要在界面元素与业务对象之间同步数据,如果用户界面包含的同步任务越多,则用户界面越复杂。
 

MVC

MVP

MVVM

 

V

C

V

P

V

VM

状态

 

   

逻辑

 

 

 

同步

 

 

表现设计模式 (Presentation Design Pattern)

  表现设计模式有助于解决上面列出的问题, 它的的基本逻辑就是创建一个额外的表现类 (Presenter) ,用来消化用户界面中复杂的逻辑,数据和同步的问题,从而使得用户界面变得简单明了。根据这个类承担责任的多少,决定了表现设计模式的类型,可能是 SC , PV , PM 等,也就是说,这个类的成熟度决定了它将是那种设计模式。

表现模型 (PM)

    1. ·表现类包含逻辑
    2. ·表现类包含状态
    3. ·表现类代表抽象的用户界面
    4. ·表现类不关注用户界面
    5. ·视图关注表现类
    6. ·视图与业务模型完全隔离 

 

 MVVM 

  • 继承自表现模型
  • 使用 WPF 以及 Silverlight 的绑定机制

MVC 

  • 没有表现类,有控制器 (Controller)
  • 请求首先到达控制器
  • 控制器负责绑定视图与业务模型
  • 逻辑存在于控制器中

 什么是 MVVM

  MVVM的提出源于WPF,主要是用于分离应用界面层和业务逻辑层,以前的ASP.Net三层架构是Web架构,MVVM是Windows应用程序架构。WPF用Xaml绘制界面,绘制完的界面是个独立的文件,文件里包含界面所有的样式和行为(行为是对行动的一种封装)。

为什么使用 MVVM

  MVVM实际上是三层架构,M层(Model实体层)、V层(View表示层,它有DataContext属性,这个属性可以使用DataTemplate模板绑定VM层的数据用来显示)、VM层(ViewModel层,对Model层进行CRUD进行操作,同时对V层提供数据绑定)。

 

命令对象

 漫谈消息 

 triggers,actions,behaviors

  

 

 

 

 

 

 

 

  传统的WinForm和ASP.NET应用程序是基于事件驱动开发的,以ASP.NET为例,在实际开发中,*.aspx页面用于渲染HTML,*.aspx.cs页面用于实现服务端逻辑,在开发初期,这种方式显得方便快捷,但是这种高耦合性导致了后期维护的复杂性,一旦aspx变化,aspx.cs的代码同时需要改变,比如将aspx中的GridView控件以FormView控件进行替换,对应的aspx.cs文件中不得不进行大量修改。而MVVM模式在WPF/SL应用程序中得以广泛应用的原因是,WPF/Siverlight应用程序是基于数据驱动的开发的,网上曾有研究者在WinForm下实现MVVM模式与WPF进行对比,得出结论:WinForm项目中大规模运用MVVM模式开发效率很低。

  与MVC,MVP所不同的是,MVVM的引入不仅仅是技术上解除耦合应对变化的原因,另外一个很大原因是:软件团队开发方式的改变.

  为什么MVC/MVP模式不行而MVVM可以呢? 很简单, 在MVC和MVP模式中, View层都具有很多代码逻辑, 开发View层的是程序员, 虽然UI/UE团队会做很多工作, 但这个层的"实现者"仍然是程序员. 在以前的开发中,其工作得很好, 而在WPF开发中程序员对View层的展现显得力不从心了,美工虽然很擅长, 但他会说"可惜我不会程序".于是, 我们需要一种方式将View层的代码逻辑抽取出来,并View层很纯粹以便完全让美工去打造它.相应地, 需要将View层的相应逻辑抽取到一个代码层上,以便让程序员专注在这里。

  我们只所以要在View(Xaml)背后写一些代码(C#), 无非是想传递一些数据以及传递数据时的数据的处理或在用户与界面控件进行交互时执行一些操作, 最简单的例子是在MVC中当界面发生交互时View去调用Controler中的某个方法, 以便将该操作的相应"指示"传递到"后台"去. 在以前的技术中, 这样的"衔接性"的代码是必须的.而在WPF中, 则可以通过另外的技术来进行层与层之间的"衔接", 这就是"Binding" 、"Command"、"AttachBehavior".通过Binding,我们可以实现数据的传递; 通过Command,我们可以实现操作的调用.Binding和Command是可以写在XAML中的, 这样看来XAML后面对于的CS文件可以被完全抛弃或不予理会了.这样的XAML文件正是美工所需要的. 而这些对于Binding以及Command的定义描述以及其他相关信息的代码应该放在那里呢,当然不是View, 更不是Model, 是"ViewModel". ViewModel是为这个View所量身定制的, 它包含了Binding是所需的相关信息,比如Converter以及为View的Binding提供DataContext, 它包含了Command的定义以便View层可以直接使用,另外,它还是一个变种的Controler, 它得负责业务流程的调度。

  在WPF/Silverlight中应用MVVM模式,View主要用于界面呈现,ViewModel用于逻辑实现,Model用于数据的构造,而这三者能够进行通信,最重要的是通过WPF/Silverlight中强大的数据绑定机制,将View和ViewModel有效的联系起来。

  尽管在MVVM模式的名称没有体现Command,但是在实际情形中,Command是实现MVVM至关重要的一环,目前项目主要采用了Prism框架中的DelegateCommand<T>类

  在Silverlight项目中采用MVVM模式,优势是显而易见的:

  1对于视图-逻辑的分离便于后期对原有功能扩展和维护,当UI变化时,ViewModel中的逻辑不需要进行变化

  2可以仅仅通过Blend实现简单的功能,而不需要写任何代码。

  在实现过程中,不要只是为了实现MVVM而MVVM,而应该根据实际情况进行取舍,事实上,由于Silverlight只是WPF的一个子集,其对MVVM模式在某些方面的支持仍有所欠缺:

  1 对枚举类型绑定比较困难,如将枚举类型绑定至RadioButton

  2 Silverlight4中仅仅对继承ButtonBase的控件实现了Command属性,在实际的使用中,对于其它的事件可以使用Blend4中中的InvokeCommandAction

  3 无法在View和ViewModel传递复杂对象,可以破坏View或者ViewModel作为折衷办法,如ChildWindow和父容器的对象传递

 有很多MVVM框架可以做到这点,其中一些是:

开源的

  • PRISM:由微软提供,和MEF/Unity一起用于依赖注入,支持组合命令,可以扩展MSDN上有详细的教程和演练。 
  • MVVM Light Toolkit:有visual Studio和Expression Blend的项目和项的模板。更多信息请看这里,另外可以参考VSExpression Blend的使用教程。
  • Caliburn Micro:支持视图模型先行(ViewModel-First)和视图先行(View-First)两种开发方式,通过co-routine支持异步编程。
  • Simple MVVM Toolkit:提供VS项目和项的模板,依赖注入,支持深拷贝以及模型和视图模型之间的属性关联。
  • Catel:包含项目和项的模板,用户控件和企业类库。支持动态视图模型注入,视图模型的延迟加载和验证。还支持WP7专用的视图模型服务。

闭源的

  • Intersoft ClientUI:付费的,只支持WPF和Silverlight,但是,除了MVVM框架,它还提供其它一些特性
  • Vidyano:免费但不开源。带有实体映射/虚拟持久化对象(数据容器),业务规则以及内置基于ACL的安全特性。

若想了解MVVM,可以参考以下资料:

使用MVVM的最大好处之一是分离关注点,以便用户体验设计师和应用程序开发者可以并行工作。另一方面,相关的担忧包括它对于UI操作比较简单的情况有点杀鸡用牛刀的感觉,数据绑定有点难以调试,以及大量使用数据绑定可能带来性能问题等等。

  Jonathan Allen在评论里提到几点错误使用MVVM的征兆:

1. 你的模型和视图模型名字相同。

  视图模型不应该是对模型的包装。视图模型的职责是外部服务的请求中介,比如加载和保存数据。而数据本身,以及验证和大多数业务逻辑应该放在模型里。

我经常强调这点。每当你创建一个视图模型包装一个模型,你就在你的API里引入一个巨大漏洞。具体地,任何直接引用这个模型的东西都可能以视图模型无法察觉的方式改变某个属性,因此UI也不会有相应的改变。同样地,模型里计算字段的任何更改也不会回传给视图模型。

2. 你的视图和视图模型名字相同。

  理想的情况下,视图模型是不知道使用它们的视图的,尤其是WPF应用程序有多个窗口共享相同的视图模型。

对于比较小型的应用程序来说,整个应用程序可能只需一个视图模型。对于比较大型的应用程序来说,主要功能可能需要一个视图模型,每个次要方面也需要一个,比如配置管理。

3. 你没有代码隐藏。

  代码隐藏既非一个好的东西,亦非一个坏的东西。它只是一个用来放置和视图或控件相关的逻辑的地方。因此,当我看到一个视图没有任何代码隐藏,我就会马上检查是否存在以下问题:

  • 视图模型是否通过名字接触了特定的控件?
  • 视图模型是否通过命令参数访问控件?
  • 是否使用了EventToCommand或其它可以导致泄露的行为而不是简单的事件处理程序?

MVVM Light的EventToCommand很有问题,因为它会使得控件从屏幕移除之后无法被垃圾回收。

4. 视图模型监听属性更改通知

  如果一个模型的的生命周期比监听它的事件的视图模型长,那么可能导致内存泄露。不同于视图有个Unloaded事件,视图模型对于生命周期管理没有很好的方案。因此如果它们关联到存活期比它们更长的视图模型的事件,视图模型将会出现泄露。

 

参考:

posted @ 2012-05-15 15:15  DebugLZQ  阅读(3321)  评论(10编辑  收藏  举报