MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式。以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解。现在一边查资料,并结合自己的理解,来谈一下对于这三种模式思想的理解,以及它们的区别。
一,MVC, MVP, MVVM诞生的需求?
软件中最核心的,最基本的东西是什么?是的,是数据。我们写的所有代码,都是围绕数据的。
围绕着数据的产生、修改等变化,出现了业务逻辑。
围绕着数据的显示,出现了不同的界面技术。
没有很好设计的代码,常常就会出现数据层(持久层)和业务逻辑层还有界面代码耦合的情况。
ORM等框架,解耦合了业务逻辑和数据之间的耦合,业务逻辑不再关心底层数据如何存储和读取。所有数据呈现给业务逻辑层的就是一个个的对象。
而MVC, MVP, MMVM用来解决业务逻辑和视图之间的耦合。
二,一段典型的耦合代码
{
SqlDataAdapter adapter = new SqlDataAdapter("select * from Table1","server=.;database=db;uid=sa;pwd=password");
DataSet ds = new DataSet("ds1");
adapter.Fill(ds);
this.GridView1.DataSource = ds;
this.GridView1.DataBind;
}
上面的这段代码中,既包含了数据访问,还包含的页面展示。当项目复杂程度更高,这种代码就会变得非常难以维护,层次也不清晰。
三,MVC模式
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式
3.1主动MVC
MVC的理论思想对应的是主动MVC,这里的主动的意思是, Model会主动通知View更新。而我们使用MVC框架, Struts, asp.net mvc等都不是主动MVC(视图的更新都是通过Controller完成的)
Model
用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。
模型中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改变。
View
视图层负责数据的展示。
在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里订阅Model的事件。
Controller
控制器是M和V之间的连接器,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据模型上的改变。
3.2被动MVC
下图是被动MVC中的流程,和主动MVC不同之处是, View没有订阅Model数据变化的事件,等待Model来通知需要根据新的数据来更新View.在被动MVC中,Controller负责通知View,有数据变化,需要更新视图。
被动MVC中,与主动MVC的区别在于:
1、模型对视图和控制器一无所知,它仅仅是被它们使用
2、控制器使用视图,并通知它更新数据显示
3、视图仅仅是在控制器通知它去模型取数据的时候它才这么做(视图并不会订阅或监视模型的更新)
3.3. Web应用中的MVC框架
Web中的MVC框架都是被动MVC模式,因为web应用中,由于http是基于请求和响应方式协同工作的,因此当服务器端的model发生变化时,它不会立即更新客户端的view,只有客户端重新请求或刷新页面时才更新.
下图是典型的MVC框架中的MVC一个请求流程。
3.4 MVC总结
MVC优点
MVC使用的误区
1.把Model理解成实体类(Entity),在MVC中Model应该包含2部分功能,一部分是处理业务逻辑,一部分是提供View显示的数据
2.把业务逻辑全部放在Controller端这两个误区本质上都是对Model的作用不明导致的。Model在MVC架构中起的作用非常重要,它应该是业务逻辑真正的实现层。所以Model的实际上是Business Model(业务模型)。而Controller仅仅起一个“桥梁”作用,它负责把View的请求转发给Model,再负责把Model处理结束的消息通知View。Controller是用来解耦View和Model的,具体一点说,就是为了让UI与逻辑分离(界面与代码分离)。
引自http://www.techopedia.com/definition/27454/model-mvc-aspnet
Techopedia explains Model (MVC)
The Model is the part of MVC which implements the domain logic. In simple terms, this logic is used to handle the data passed between thedatabase and the user interface (UI).
The Model is known as domain object or domain entity.
The domain objects are stored under the Models folder in ASP.NET. Thedomain model represents the application perspective for the data to be handled whereas a view model is required to produce the engine that generates the View.
This definition was written in the context of ASP.NET.
MVC的缺点
完美的MVC应用场景应该是这样的:
有个Student Model,关联StudentListView, StudentEditView.
对于StudentListView, Student Model提供Student的集合数据来显示StudentListView
对于StudentEditView, Student Model提供单个Student数据来展示StudentEditView并且响应StudentEditView的保存操作。
但是这只是完美的情况,实际应用中,在ListView上,不单单显示Student的信息,可能还需要这个Student的历史成绩,家庭情况,老师信息。而这些是Student Model不能提供的。
也许我们可以扩展Student Model,将Student Model能够提供的信息扩展,包含成绩信息等,这本身也可以。但是,如果Student显示的View,这个需要只是需要额外的成绩信息,另一个View只是需要额外的家庭信息,Student Model是不是有些疲于奔命,你能知道还会有多少个差异化的View的需求?而且让逻辑端代码这样不断的修改来适应View端,好吗?
由于MVC的设计思想是从Model出发,而没有考虑到View端的复杂性,这样导致的问题是Model难以符合复杂多变的View端变化。
相对这点,MVP和MVVM就要好得多。它们都独立出了Presenter和ViewModel来对应每个View。
四、MVP模式
MVP模式也是一种经典的界面模式。MVP中的M代表Model, V是View, P是Presenter。
下面例子中的完整代码: WinformMVP源码(http://files.cnblogs.com/JustRun1983/WinFormMVP.zip)
大家还可以比较园中Artech的这篇文章谈谈关于MVP模式中V-P交互问题(http://www.cnblogs.com/artech/archive/2010/03/25/1696205.html)
4.1 MVP的思想
MVP模式在我看来,是一个真正意义上的隔离View的细节和复杂性的模式。为什么这么说:
因为在其它模式中V都代表的是UI界面,是一个html页面,XAML文件或者winform界面。但是在MVP模式中的V代表的是一个接口,一个将UI界面提炼而抽象出来的接口。接口意味着任何实现了该接口的界面,都能够复用已有的Presenter和Model代码。
4.2 UI界面接口化
要很好的理解MVP,就要有把UI界面接口化的能力。看下面的界面中,将红色标记的User Control抽象一下,就能得到下面的接口
public interface IUserAdd
{
event EventHandler UserAddEvent;
string UserName { get; set; }
string UserAge { get; set; }
}
界面中的2个输入框被抽象成了UserName和UserAge两个属性。Save按钮的点击事件,被抽象成了事件UserAddEvent。winform中实现该接口的代码如下:
public partial class UserAdd : UserControl, IUserAdd
{
public event EventHandler UserAddEvent;
public string UserName
{
set { this.txbName.Text = value; }
get { return this.txbName.Text; }
}
public string UserAge
{
set { this.txbAge.Text = value; }
get { return this.txbAge.Text; }
}
public UserAdd
{
InitializeComponent;
}
private void btnAdd_Click(object sender, EventArgs e)
{
if (UserAddEvent != null) UserAddEvent(this, e);
}
}
下面拿UserAge属性来解释一下,UI界面接口化的魔力。当后端代码要获取界面上的年龄值,就只需要get属性,要更新界面显示的时候,就只需要set属性。
这个时候,后端代码对于界面的操作,被抽象成了对于UserAge属性的操作了,也就是和具体的界面显示无关了。
4.3 Presenter —— Model和View之间的桥梁
上文提到的后端代码中,包含了P和M. M和MVC中一样,指的是逻辑代码。P则是Model和View之间的桥梁,负责将对应的Model和View组合到一起。
针对上面的IUserAdd,对应的Presenter代码是:
public class UserAddPresenter:IPresenter
{
private readonly IUser _model;
private readonly IUserAdd _view;
private readonly ApplicationFacade _facade = ApplicationFacade.Instance; //这里的facade是Presenter之间通信用的,详细可以看完整代码
//Presenter构造函数中,将view和model作为参数传入
public UserAddPresenter(IUser model, IUserAdd view)
{
_model = model;
_view = view;
WireUpViewEvents;
}
private void WireUpViewEvents
{
_view.UserAddEvent += _view_UserAdd;
}
//当view的UserAdd事件触发,取得UI中的数据,调用model逻辑处理,添加新用户。
//同时发送User_ADDED消息到系统中(系统中其它UI部分接收消息,比如这里的DataGrid,它接收到User_ADDED之后,会刷新)
private void _view_UserAdd(object sender, EventArgs e)
{
var user = new User
{
Name = _view.UserName,
Age = Convert.ToInt32(_view.UserAge)
};
_model.AddItem(user);
_facade.SendNotification(ApplicationFacade.USER_ADDED);
}
}
4.4 MVP的代码结构和时序图
这里的MVP中的代码结构图和时序图,能够更好的帮助理解MVP模式
4.5 MVP模式总结
在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。
而且,Presenter与具体的 View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用!
不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试 ——而不需要使用自动化的测试工具。
我们甚至可以在Model和View都没有完成时候,就可以通过编写MockObject(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。
MVP的优势
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presener用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户界面来测试这些逻辑(单元测试)
五, MVVM模式
5.1 MVVM模式的设计思想
MVVM模式中,一个ViewModel和一个View匹配,它没有MVP中的IView接口,而是完全的和View绑定,所有View中的修改变化,都会自动更新到ViewModel中,同时ViewModel的任何变化也会自动同步到View上显示。
这种自动同步之所以能够的原因是ViewModel中的属性都实现了observable这样的接口,也就是说当使用属性的set的方法,都会同时触发属性修改的事件,使绑定的UI自动刷新。
(在WPF中,这个observable接口是 INotifyPropertyChanged;在knockoutjs中,是通过函数ko.observable和ko.observrableCollection来实现的)
所以MVVM比MVP更升级一步,在MVP中,V是接口IView,解决对于界面UI的耦合;而MVVM干脆直接使用ViewModel和UI无缝结合, ViewModel直接就能代表UI.但是MVVM做到这点是要依赖具体的平台和技术实现的,比如WPF和knockoutjs,这也就是为什么ViewModel不需要实现接口的原因,因为对于具体平台和技术的依赖,本质上使用MVVM模式就是不能替换UI的使用平台的.
5.2 MVVM模式结构图
这里是MVVM模式的结构图,能够帮助更加容易的理解MVVM模式:
六, MVC, MVP和MVVM模式使用场景总结
由于在winform中无法像WPF一样,支持数据和界面的双向绑定以及事件的监控,所以,在winform中MVP是很好选择。
WPF和html界面中使用Knockout,实现了observable,所以使用MVVM.(应该说WPF就是为使用MVVM设计的)
在web应用中,由于http是基于请求和响应方式协同工作的,无法一直保持连接状态,所以无法达到MVP中Presenter之间的消息传递和MVVM中的ViewModel和界面之间的绑定,所以MVC是很好的选择。
关于:中科研拓
深圳市中科研拓科技有限公司专注提供软件外包、软件开发、软件定制、app外包、智能硬件开发、O2O电商平台、手机应用程序、大数据系统、棋牌游戏、物联网项目等开发外包服务,通过IT技术实现创造客户和社会的价值,成为优秀的软件公司,通过客户需求导向、开放式创新、卓越运营管理等战略的实施,全面打造公司的核心竞争力。优秀软件外包公司、软件开发公司,联系电话400-0316-532,邮箱sales@zhongkerd.com,网址www.zhongkerd.com