深入了解 MVVM






4.67/5 (42投票s)
这篇关于MVVM架构模式的文章,让开发者能够区分松耦合和紧耦合的应用。
引言
这篇关于MVVM架构模式的文章,让开发者能够区分松耦合和紧耦合的应用。
背景
在MVVM出现之前,一些架构模式在设计上是紧耦合的。为了克服这种缺点,MVVM应运而生。
Using the Code
问题陈述
什么是松耦合系统设计?如何在WPF或Silverlight应用程序中实现低耦合度?
解决方案
松耦合应用
耦合度是一个类对另一个类的依赖程度,从低到高不等。在紧耦合系统中,一个类的变化会迫使另一个类发生变化,增加了修改的难度。紧耦合会增加维护成本,降低组件的可重用性。因此,为了实现稳定且低维护成本的应用程序,低耦合度是系统设计的一个理想特性。
松耦合应用旨在实现职责分离和低耦合度。架构模式用于减少对象之间的耦合,而架构模式则有助于改进系统的设计。
松耦合的主要优点在于其可在不以不可预测的方式破坏其他模块的情况下进行更改。下图展示了一种示例方法,设计师和开发人员如何以独立的方式工作以实现松耦合系统。
松耦合应用的优点
- 易于维护:松耦合应用能够在不影响其他子系统或服务的情况下更改某个子系统或服务的实现。它允许UI设计人员和开发人员独立工作。这意味着,如果将来视图(外观和感觉)被另一个视图替换,则不需要更改ViewModel和Model。
- 易于测试:松耦合应用可以通过许多可用的单元测试框架(如Nunit等)轻松进行测试。
设计松耦合应用的挑战
在设计复杂的UI应用程序时,UI会遇到以下四个问题:
- 状态:Web应用程序使用会话变量来维护状态,而在Windows应用程序中,则是简单的UI级别数据。维护状态的工作越多,它就越复杂。
- 逻辑:控制用户界面的操作逻辑越多,其复杂度就越高。
- 同步:UI控件和业务对象之间的协调和同步越多,复杂度就越高。
- 缓存:频繁访问应用程序的数据从数据库检索是最慢的方式,并且会严重影响性能。因此,缓存是消除性能瓶颈的根本方法,从而解决缓慢的数据访问问题。对于单层应用程序,可以在UI级别(XAML、主题、图像)、业务规则和Entity-Framework缓存上创建不同类型的缓存对象,这些缓存对象将保留在同一页面上。理想情况下,对于N层应用程序,缓存数据应尽可能靠近将使用它的代码。因此,表示层将具有缓存UI的XAML、主题、图像;业务层将具有快速使用的流程和业务规则;数据层将缓存频繁使用的Entity-Framework数据对象。
上述问题已由某些架构模式(如MVC(Model-View-Controller)、MVP(Model-View-Presenter)和MVVM(Model-View-ViewModel))解决。
以下是解决上述问题的架构模式:
MVC(Model-View-Controller)
MVC模式的主要目标是将视图与实际的数据处理分离,以便同一模型可以用于多个视图。这是通过使用三个不同的对象(Model
、View
和Controller
)来实现的,它们以松耦合的方式相互交互。MVC模式仅用于Web(ASP.NET)窗体。
- Controller:用户与控制器交互,控制器再命令模型和视图进行更改。它负责通知动作。
- View:视图负责外观和感觉,并与复杂的数据处理分离。视图可以是XML/XSLT、网页或HTML。
- Model:模型响应控制器发出的请求,并通知视图更新其外观。简而言之,模型是显示中立的。
MVP(Model-View-Presenter)
提出了一种新方法,用Presenter替换Controller,用户请求被定向到View而不是Presenter。关联的Presenter和View具有一对一的映射关系,即前者直接引用View。Presenter会更新(与之关联的)View以响应请求的操作,而这些操作作用于Model。MVP模式仅用于Windows窗体。
不同层及其关系的详细信息
- 表示层逻辑:表示层逻辑主要关注以一致且易于理解的方式向最终用户可视化信息。下面列出了设计模式中的功能实现,包括UI平台的具体实现。
设计模式 UI平台 MVC ASP.NET Web Forms MVP Windows Forms MVVM WPF/Silverlight应用 - SOA至业务逻辑:MVC、MVP和MVVM等UI平台会消耗SOA下托管的服务。服务层接收来自表示层逻辑的消息,并解释消息,解包数据传输对象(DTO),并编排和协调业务对象和数据访问对象之间的交互。服务层是实现授权、身份验证、数据库事务和数据验证的地方。
- 业务逻辑:业务逻辑包含封装业务规则形式的业务对象的业务逻辑。业务对象不知道数据库或数据持久化。
- 业务逻辑至数据逻辑:业务对象不直接与数据逻辑交互;而是由专门的数据访问对象处理,以从业务对象中提取数据并将其保存到数据库中。
- 数据逻辑:数据逻辑处理业务对象的持久化。ADO.NET和LINQ-to-SQL是设计模式中使用的两种不同技术。ADO.NET包含用于连接数据库、执行命令和检索结果的.NET Framework数据提供程序。使用LINQ-to-SQL时,实体被映射到业务对象。
因此,表示层逻辑与业务逻辑或数据逻辑之间没有直接交互。表示层通过SOA(服务)与整个应用程序进行交互,SOA管理安全性、数据验证和事务。因此,所有与应用程序的通信都必须通过托管在服务下的单个入口点。
MVVM(Model-View-ViewModel)
Model-View-ViewModel(MVVM)架构模式方法将应用程序的业务逻辑和表示逻辑与用户界面(UI)分离。通过保持应用程序逻辑和用户界面(UI)之间的分离,有助于解决设计和开发问题,从而使应用程序更易于测试和维护。它还可以大大提高代码的可重用性,使UI设计人员和开发人员在开发应用程序的各自部分时能够更轻松地协作。
MVVM设计模式,应用程序的UI和底层表示逻辑以及业务逻辑可以分为三个独立的类:View,封装UI和UI逻辑;ViewModel,封装表示逻辑;Model,封装应用程序的业务逻辑和数据。
MVVM模式用于WPF/Silverlight应用程序。
MVVM架构详情
MVVM架构模式的引入是为了在WPF/Silverlight应用程序中实现View、ViewModel和Model的隔离、事件驱动编程方法和灵活的可测试性。MVVM是MVP(Model View Presenter)设计模式的一个特殊且接近的变体,因为在MVVM中,Presenter被ViewModel替换。Model通过通知与ViewModel通信,ViewModel通过命令绑定和数据绑定与View通信。
类职责和特性
View 类
View
类的职责是定义用户在屏幕上看到的结构和外观。它是一个视觉元素,例如页面、用户控件、窗口或数据模板。它利用Silverlight/WPF的主要数据绑定功能,将ViewModel中定义的属性绑定到View所组成的aught上的用户控件。
View捕获的用户事件通过Commands发送到ViewModel。现在,这些命令会执行ViewModel中包含逻辑的方法。通过将View绑定到ViewModel的属性来更新View中的数据。
根据MVVM合规应用程序,View应包含最少的代码,在特殊情况下,代码应仅用于特定视图。它包含UI控件和ViewModel中定义的属性到控件的绑定,这些绑定也是View特有的。View中也提及了View特有的验证代码。View与ViewModel之间存在多对一的关系。为了代码重用,使用了User Control,并在View中设置了ViewModel的DataContext。样式也仅在View中提供。简而言之,View独立于ViewModel,反之亦然。
Model 类
Model
类负责数据表示。它继承INotifyPropertyChanged
接口,以便在Model
中定义的属性被修改时通知ViewModel
。它用于以一种易于View
和ViewModel
管理和使用的方式公开数据对象。像IDataErrorInfo
这样的对象会被继承到Model
类中,以提供Model
中定义的属性的验证。对于onPropertyChangedEventHandler
中的每个属性,还会在Model
中添加RaisePropertyChanged
方法。
ViewModel 类
MVVM设计模式中的ViewModel
封装了View的表示逻辑和数据。它仅仅持有从数据层检索的数据,View持有格式化的信息,而ViewModel则充当两者之间的联络人。它从不与View紧密耦合,如果ViewModel中的任何属性发生更改,它会通知View。它可能会从View接收输入并将其放入Model,或者它可能与服务交互以检索Model,然后转换属性并将其放入View。它还公开了方法、命令,并帮助维护View的状态,作为View上操作的结果来操纵Model,并触发View本身的事件。ViewModel始终更新Model,并且View中的属性通过双向数据绑定映射到ViewModel中的命令,这些属性由UI级别的事件生成。
MVVM 实战
视图
下面的屏幕截图涵盖了View特有的UI级验证,以显示Model中引发的验证的外观和感觉。用户控件中的TextBox
控件是这些验证的目标。
模型
下面的屏幕截图涵盖了Model中定义的属性特有的服务器端验证。通过属性来验证输入到文本框中的值。
ViewModel
下面的屏幕截图涵盖了命令对象,例如:继承自ICommand
的RelayCommand
。RelayCommand
引入了命令,它允许将用户事件(如按钮单击事件)绑定到执行逻辑。每个用户定义的命令都派生自ICommand
。为每个命令创建新的ICommand
可能会导致代码重复。因此,RelayCommand
是实现可重用性的一种更好的方法。它是一个泛型类,实现了ICommand
以提供简单小型命令声明方法。ICommand
用于通知用户在UI上执行了操作。ICommand
接口有三个操作:Execute
、CanExecute
和CanExecuteChanged
。Execute
是控件的事件处理程序例程,CanExecute
是确定控件是否启用的方法,而CanExecuteChanged
是一个通知UI检查控件是否应启用的事件。执行逻辑和“can-execute”逻辑在构造函数中引入。
单元测试:View-ViewModel的分离允许在没有ViewModel的情况下对View进行单元测试。由于ViewModel负责与应用程序的交互,因此这些单元测试用例将涵盖最重要的功能,而View的测试用例只需包含外观和感觉特性的测试,例如数据格式化等。下图是MVVM合规应用程序的屏幕截图,其中单元测试项目涵盖了Model、View和ViewModel级别定义的所有方法的单元测试用例。
MVVM的优点
灵活性:同一个ViewModel
可以使用多个View,这允许根据不同用户的需求,为相同的功能实现完全不同的外观和感觉。
可重用性:由于UI和代码级别功能的隔离,View和ViewModel都有更高的重用潜力。
低耦合度:UI设计和功能开发的分离使得开发人员的工作和设计师的工作可以分开。
测试:由于低耦合度,单元测试非常可行,因为UI测试将涵盖视觉特性(如颜色、字体样式)的测试,而功能测试用例将分别涵盖ViewModel
中的类。
MVVM的缺点
尽管MVVM架构模式是复杂应用程序的完美方法,但对于简单应用程序而言,它需要最佳的编码实践和标准。此外,对于简单应用程序,没有可重用性、可扩展性和可伸缩性的空间,也就是说,MVVM不适用于简单应用程序。
MVVM 合规应用程序的规则
有几条规则可以使应用程序符合MVVM。
- 最小化的后台代码:View.xaml.cs甚至事件处理程序都应该没有代码。MVVM合规应用程序的后台代码应最小化。在特殊情况下,特定View的代码可以驻留在后台代码中,例如代码完全面向View,与Model、ViewModel或其他View无关。依赖属性和任何不符合MVVM的第三方控件可以作为后台代码的一部分。
- 事件即命令:控件生成的所有事件都应视为命令。
- ViewModel作为DataContext:根据MVVM合规应用程序,ViewModel没有View的实例,View也没有ViewModel的实例。根据最佳实践,无需将
ViewModel.DataContext
类型转换为ViewModel。 - 独立设计ViewModel和View:为了实现MVVM合规,View和ViewModel应独立设计,以保持低耦合度。
WPF/Silverlight应用程序中的最佳实践:PRISM
Prism提供了一种方法,可以更轻松地设计、构建灵活、丰富且易于维护的WPF桌面应用程序、Silverlight Rich Internet Applications和Windows Phone应用程序。
Prism通过考虑诸如关注点分离和松耦合等重要的架构原则,帮助设计和开发复合WPF/Silverlight应用程序,以便松耦合的组件可以独立演进,并可以轻松无缝地集成到整个应用程序中。这些类型的应用程序称为复合应用程序。
Prism应用程序的组件
PRISM应用程序的主要组件是:
- Shell:Shell是一个定义应用程序用户界面结构的模板。
- Regions:Shell包含不同的区域,视图在运行时注入到这些区域中。
- Modules:模块通常是应用程序的单个功能部分。模块通常不应依赖于其他模块。
- Views:模块包含不同的视图,它们定义了应用程序的用户界面。MVVM通常用于PRISM中设计视图。
- Bootstrapper:
Bootstrapper
是一个负责初始化PRISM应用程序的类。它允许开发人员创建和配置模块目录、IOC容器、RegionAdapter
映射以及应用程序的Shell。
结论
MVVM模式是用于设计和开发复杂的WPF/Silverlight应用程序的指导性架构模式,以实现更好的可重用性、可维护性和可扩展性,并具有低耦合度。我们应避免将MVVM模式用于简单应用程序。