http://www.cnblogs.com/seaky/archive/2011/04/06/1982533.html

在深入分析MVC和MVP之前,我们有必要回顾下经典的三层架构。分层是计算机学科解决许多问题的法宝。在企业应用和互联网应用中,分层架构得到了非常广泛的应用。3层架构是各种层架构的基础,3层架构简单描述如下:

展示层:展示层有两个职责

1负责展示业务数据

2提供用户输入的接口

业务逻辑层:业务逻辑层的职责是接受展示层的输入,并经过业务处理逻辑,返回业务数据。

数据访问层:数据访问层提供系统数据的存取服务。

从架构到实现是存在一些"距离"的,架构的实现是要基于应用场景,实现方案也有所不一样。

本文考虑两种应用场景来讨论MVC和MVP:

第一是c/s架构,也就是所谓的胖客户端,像RIA属于这类。Flex,ajax等技术都可以归结到RIA,手机APP,winform连接到远程服务的应用都可以归结这一类。

第二是b/s架构(某些web程序部分的利用了ajax,排除利用ajax的这部分),也就是瘦客户端,b是指浏览器,在b/s架构中http协议是客户端和服务端通信的唯一协议,而且是通过浏览器来展示数据的。

基于层的架构的实现中的一个问题就是层与层之间如何通信,以及如何在层间传递数据。

首先看看数据访问层和业务层之间的通信和数据传递。

(注意,数据访问层不保存持久数据,数据访问层是访问持久数据的接口。持久数据一般位于独立数据服务器之中,比如数据库,索引数据等等。)

在物理上,数据访问层和业务逻辑层通常部署在同一台服务器上,所以它们之间的通信属于进程内数据传递,传递的是业务对象,速度很快,但是数据访问层 访问持久数据相对很慢的。数据访问层目标就是尽可能的快的存储业务对象,而业务模型的设计会影响业务对象的存储速度,所以数据访问层的设计和实现通常是在 业务模型和数据访问策略之间进行权衡。关于数据访问层设计也有几种模式,关于这方面请自行查阅相关资料。

下面我们看看本文的重点:

业务层和视图层的通信,我们常用的是MVC模式。MVC模式是展示层模式(注:这句话是有问题的),有必要细致的分解下展示层的职责:

1 渲染界面。在b/s架构中体现在浏览器解析html然后绘制相应的图形。在c/s架构中,比如winform程序中利用控件的状态调用本地GDI接口绘制窗口,填充数据。

在b/s和c/s中,这部分工作只能在客户端完成。

2 执行展示逻辑。依据业务层返回的数据来生成渲染界面所需的数据。

举例说明下,在b/s中,假设业务层返回字符串数组,在展示层需要使用列表进行展示,那么我们必须利用这个数组生成特定的html,这个过程就是展示逻辑。

在b/s架构中,展示逻辑位于服务端。

在c/s架构中这部分工作在客户端进行。比如在客户端利用ajax获取字符串数组后,利用js生成html。这个展示逻辑是在客户端完成的。在winform程序中利用控件的方法设置相应的值。

3 获取输入。在c/s和b/s之中都通过客户端程序来获取数据,比如浏览器提供的输入框,winform提供的文本控件。在c/s和b/s中这部分工作只能客户端完成。

4 引发事件。事件是触发某种业务的的起点,业务层总是在某个事件到来时被调用,事件总是携带某些用户输入的信息。

在b/s架构中,浏览器发送http请求代表着事件,而在服务器web服务器在监听http请求到达这个事件,分析http请求携带的信息,交给相应的程序处理。

在c/s架构中,事件更好理解。比如点击按钮是个事件,文本框的内容改变可以定义为一个事件,文本框的内容可以作为事件的信息。

5 处理事件。展示层还有个非常重要的任务就是调用业务层接口,当事件发生时总有个程序会处理,这段程序会调用真正的业务层。

在b/s架构中,举个例子,一段php脚本从请求读取参数,利用参数调用业务,然后生成html,这段程序就是事件处理的程序,它是属于展示层,而不是业务层的。为什么?

在c/s架构中更加明显,像winform程序的onclick事件处理器。

在b/s架构中事件的处理是在服务端,因为http请求事件是在服务端处理,而在c/s架构中事件的处理是在客户端。

下文中分别以 职责1-5代表上面的职责,其中最重要的是2和5。

 
MVC最初是smalltalk-80中提出的请参见文章http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html 
Applications Programming in Smalltalk-80(TM): How to use Model-View-Controller (MVC).
这篇文章提出了定义了MVC各自代表什么:
M是指系统数据对象的管理者,包含业务逻辑。你可以把它想象成一个门面或者一组服务。它们的返回是业务数据,它们不关心这些数据如何被使用。
C是指控制器,用于管理用户的事件,包括将处理事件,以及通知视图如何改变。
V是指视图,负责控制数据的展示,以及发起事件。
注意V和C都是位于三层架构中的展示层。而M通常是属于业务逻辑层。
最初的MVC起源于GUI程序设计。
 
在GUI程序中,用户的点击行为都会使得操作系统产生事件,最开始的做法是在view层处理这个事件(职责5),获取并且执行展示逻辑(职责2)。
随着程序的庞大,事件处理逻辑和展示逻辑混合在一起不利于维护,所以将职责5和职责2分离到不同的对象之中,controller是执行职责5。view执行职责2。
 
 
此时view需要和controller存在双向引用,事件发生时,view将事件处理逻辑代理到controller中,controller反过来使用Model获取数据,调用view的展示逻辑方法,刷新界面。
存在被动的MVC和主动的MVC两种类型
被动的MVC的特点是controller负责通知视图去改变。而在主动的MVC之中,Model会通过观察者模式间接的和视图关联,当model变化时通知view去改变。
 
 
 
 
 
 
 
 
 
 
 

下面我们看看在b/s架构的应用环境之中MVC有着怎么样的变化。

在b/s场景之下,用户在浏览器中的任何行为导致的事件最终都会转换为http请求,所以只存在单一的事件,所以第一步是我们需要依据事件携带的信 息将这个http请求转发给相应的事件处理器。事件处理器在获取业务数据后,需要通知视图去改变,这个过程通过一个http响应来实现,而http响应体 就是视图渲染所需的信息,在利用业务数据生成http响应体的过程实际上就是执行展示逻辑的过程。由于http协议是无状态的,所以在基于http协议中 实现真正的主动MVC模式是比较困难的,一般的思路有两种:a长连接 b轮询方式

目前在b/s架构下有两种常见的实现MVC的模式,一种叫前端控制器,一种叫页面控制器。

前端控制器实现之中,存在一个类负责处理http请求事件,这个类就叫前端控制器,这个类主要工作就是依据请求信息将这些请求转发给相应的控制器来 处理事件。每个控制器处理结束之后会返回一些信息(职责5),然后前端控制器使用视图引擎来生成所需的http响应体(职责2)。前端控制器将职责2和职 责5代理给不同的类。

现在主流MVC框架都是采用这种,比如spring  mvc,zend等等。(个人十分推崇spring MVC,建议大家都去阅读下源码)

页面控制器更加好理解,每个页面一个控制器。所有该页面能够发生的事件都在这个控制器内响应。比如asp.net中的Page类是典型的页面控制 器。页面控制器的缺点是像一些公用的功能很难在各个控制器中复用,只能采取继承的方式,导致复杂的类体系,而且继承的方式是很难维护。

而在c/s架构之中,事件是多样的。点击按钮,勾选复选框等等都是事件,如何管理事件是在c/s实现mvc的核心。

在winform程序中,每个事件都有一个处理器(事件委托),这个处理器通常承担了职责2和职责5。

所以对于winform来讲,MVC实现的非常不好,在winform之中要实现职责2和职责5的分离必须抛弃winform的思维自己实现,或者借助相应的框架实现。

我们再看一下gxt(ext GWT)它提供一个MVC框架。

MVC框架中核心就是事件的分发,对于一个MVC框架应该处理的是应用事件而不是系统事件。系统事件是指用户操作中由操作系统产生的事件,而应用事件是每个应用程序自定义的一些事件,比如点击登录按钮系统事件是Click,而应用事件可能是AppEvent.LonIn。

gxt的Dispatcher类就是将应用级事件分发到各个控制器之中,在程序启动时需要将控制器加入到Dispatcher之中。

控制器Controller类包含其所支持的事件的列表,并包含相应的视图。在初始化时需要将支持的事件注册到事件列表之中。Dispatcher在分发事件时会调用

controller类的handleEvent方法,在handleEvent里面依据不同的事件来使用相应的方法来处理。controller类可以向视图发送事件。

View类包含一个Controller用于向上引发事件,view类包含其所支持的事件处理程序。

在gxt框架之中,controller类负责职责5,而view类可以用于负责职责2。很方便的做到职责的分离。

既然有了MVC,为何又提出MVP?在我看来MVP不过是MVC加上一些约束而已,MVC之中,V可以和M有联系,但是在MVP之中,V不必知道 M,在MVP之中强制要求职责2和职责5的分离以及M和V的分离。而在MVC中,某些情况下V还可以调用M来获取数据,但这样没能完全分离职责2和职责 5。

当事件到来时在MVC和MVP之中,都需要将事件分发给相应的C或者P

在C和P之中都需和M交互获取数据。但是在V之中有很大的不同,在MVC之中,V可以响应某些事件并可以调用M获取数据。

而在MVP之中,V完全是被动的,P通过V提供的展示接口来控制V并通过接口的参数传递数据给V。因为V中需要将任何事件的处理委托给P,所以V需要暴露回调接口给P。

举一个例子,MVP中登录窗口V的接口定义:


publicinterface LoginView {

    void setUserName(String username);

    String getUserName();

    void setPassword(String pwd);

    String getPassword();

    void showErrorMsg(String msg);

    void setLoginCallback(EventHandler handler);

    Widget asWidget();
}

实现


publicclass LoginViewImpl extends Composite implements LoginView {

 privatefinal TextField<String> username =new TextField<String>();
privatefinal TextField<String> password =new TextField<String>();
privatefinal Button login =new Button(); public LoginViewImpl() {
FormPanel layout =new FormPanel();
initWidget(layout);
layout.add(username);
layout.add(password);
layout.add(login);
layout.setSize(500, 500);
password.setPassword(true);
username.setFieldLabel("username:");
password.setFieldLabel("password:");
login.setText("登录");
} publicvoid setUserName(String username) {
this.username.setValue(username); } public String getUserName() {
returnthis.username.getValue();
} publicvoid setPassword(String pwd) {
this.password.setValue(pwd);
} public String getPassword() {
returnthis.password.getValue();
} publicvoid showErrorMsg(String msg) {
com.google.gwt.user.client.Window.alert(msg);
} publicvoid setLoginCallback(final EventHandler handler) {
login.addListener(Events.Select, new Listener<ButtonEvent>() {
publicvoid handleEvent(ButtonEvent be) {
handler.handle();
}
});
} }

其中EventHandler是当用户点击登录按钮时的回调。EventHandler的实现是在LoginPresenter之中的,对于LoginView的实现只需在onclick事件中回调即可。

对应的presenter代码如下:


publicclass LoginPresenter implements Presenter {

 private LoginView view;
privatestaticfinal String LOGIN_CHECK_URL ="../check"; public LoginPresenter() {
this.view =new LoginViewImpl();
view.setLoginCallback(new LoginEventHandler());
} /*
* 初始化view */
publicvoid go(HasWidgets container) {
container.clear();
container.add(view.asWidget());
} // 处理登录事件
privateclass LoginEventHandler implements EventHandler { publicvoid handle() { //调用业务层验证
} } }

前文说过MVP只不过是MVC加上约束而已,所以在实现时利用现有的MVC现有的框架,只要让V变成MVP中的V都可以看作是MVP。

MVP在b/s中的实现,以spring MVC为例,实际上当我们利用spring mvc的
ModelAndView类向view传递数据时,只要view中不包含对m的使用,就可以看作是MVP。只不过在b/s架构之中每次事件都需重新绘制整
个页面,所以这里面隐含所有的视图只包含render接口来绘制所有的部件。

MVP在c/s之中实现,在c/s之中事件到来时只需绘制部分界面,所以这里的V可以提供一个可读性较强的接口。

我们看看在GWT中如何实现MVP,在GWT之中有个EventBus的概念,EventBus是全局应用事件管理者,注意这里是全局应用事件,这
是指在不同view中都可以引发的事件。某些事件不需在跨view引发就不要放在EventBus之中。EventBus管理事件和对应的事件的处理程
序。

通常存在AppController类来负责初始化EventBus,并负责控制整个程序的流程。

Presenter类类似controller,负责展示相应的view,并处理view中引发的事件。

view,每个presenter都有一个自定义的view接口,view都是被动的被presenter使用。

总结,任何模式的核心都是隔离变化,为了隔离变化必须分离职责。所以MVC和MVP从本质都是一样,理解每个部分的职责才能真正理解MVC和MVP。

深入理解MVC与MVP的更多相关文章

  1. 理解MVC,MVP和MVVM设计模式

    有3个非常受欢迎的MV-*系列设计模式:MVC,MVP,MVVM.他们被广泛应用于不多种结束.这篇文章我回阐述我自己对这3个设计模式的看法. MVC模式: MVC即Model-VIew-Control ...

  2. js架构设计模式——你对MVC、MVP、MVVM 三种组合模式分别有什么样的理解?

    你对MVC.MVP.MVVM 三种组合模式分别有什么样的理解? MVC(Model-View-Controller)MVP(Model-View-Presenter)MVVM(Model-View-V ...

  3. 关于MVC与MVP的理解

    1. MVC的理解误区 理解误区: 1. 认为Model是指失血模型的实体类(Entity),是作为View和Controller之间的传输数据. 2. 把业务逻辑全部放在Controller端,认为 ...

  4. mvc、mvp和mvvm理解

    MVC.MVP.MVVM这些模式是为了解决开发过程中的实际问题而提出来的,目前作为主流的几种架构模式而被广泛使用. 一.MVC(Model-View-Controller) MVC是比较直观的架构模式 ...

  5. 谈谈对MVC、MVP和MVVM的理解

    刚出来工作的时候维护过一个老系统,该系统是用微软的ASP(Active Server Pages 动态服务器页面)写的.每一个页面都是一个ASP文件,每一个一个ASP文件中又同时包含了HTML.CSS ...

  6. 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM

    刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下 ...

  7. 转:界面之下:还原真实的 MVC、MVP、MVVM 模式

    前言 做客户端开发.前端开发对MVC.MVP.MVVM这些名词不了解也应该大致听过,都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式.网上很多文章关于这方面的讨论比较杂乱,各种MV*模式 ...

  8. UWP开发之Mvvmlight实践二:Mvvmlight的核心框架MVVM与MVC、MVP的区别(图文详解)

    最近UWP开发在海外很潮流,随着微软收购Xamarin,我们这些C#程序员也可以靠这杆小米枪挑战Android,IOS平台了. 那我们为什么选择MVVM做UWP开发?MVC,MVP,MVVM他们之间到 ...

  9. 【转】对MVC、MVP、MVVM的懂得

    [转]对MVC.MVP.MVVM的懂得 转载地址:http://www.myexception.cn/vc-mfc/1612241.html 对MVC.MVP.MVVM的理解 最近看了一堆js框架的文 ...

随机推荐

  1. ExtJS4.x 开发环境搭建

    需要的资源 ExtJS4.2 eclipse 开发环境搭建 在项目中国需要引用的文件: eclipse中有报错.需要处理的是ext-lang-zh_CN.js,中文编码不能识别.右键->属性-& ...

  2. [转]IIS部署托管管道模式的集成和经典区别

    关于ESPS和SCSJ在Windows server 2008的问题总结 SCSJ出现的问题在于集成模式和经典模式的选择上,系统本身是没有问题的.我们在部署系统的时候,选择了集成模式,导致WebCon ...

  3. iOS 进阶 第十七天(0420)

    0420 凡是继承了UIResponder的类都可以做响应者 响应事件的传递是由底到高来传递,响应者链条是由高到底来响应 相应事件的传递(由底到高 找到正在和用户触摸交互的view) 准则:事件由父控 ...

  4. oracle增加表空间的四种方法,查询表空间使用情况

    增加表空间大小的四种方法Meathod1:给表空间增加数据文件ALTER TABLESPACE app_data ADD DATAFILE'D:\ORACLE\PRODUCT\10.2.0\ORADA ...

  5. DataTemplate和ControlTemplate联系与区别

    ---恢复内容开始--- 正如标题中的两个拼接的单词所说,DataTemplate就是数据显示的模板,而ControlTemplate是控件自身的模板.(个人理解,错误请指出,谢谢) 我们看这二者在两 ...

  6. windows多线程编程(一)(转)

    源出处:http://www.cnblogs.com/TenosDoIt/archive/2013/04/15/3022036.html CreateThread:Windows的API函数(SDK函 ...

  7. Teamwork——Week 4 Daily Scrum Meeting#1 2013.10.23

    一.会议议题 1)根据确立的项目题目,进一步明确PM,DEV,TEST的工作. 2)确定团队分工和预估项目时间. 3)完成项目架构NABC模型. 4)确定第一轮开发团队分工 二.会议时间 2013年1 ...

  8. Gentoo安装配置过程与总结

    前些时间在VMware上安装了Gentoo Linux,用了当前最新版的Gentoo,安装过程记录下来了,但一直没有整理到blog上.今天重新整理一下,写出来与大家分享和备用.接触Gentoo不久,对 ...

  9. bzoj 2743 树状数组离线查询

    我们按照询问的右端点排序,然后对于每一个位置,记录同颜色 上一个出现的位置,每次将上上位置出现的+1,上次出现的-1,然后 用树状数组维护就好了 /************************** ...

  10. Leetcode#123 Best Time to Buy and Sell Stock III

    原题地址 最直观的想法就是划分成两个子问题,每个子问题变成了:求在某个范围内交易一次的最大利润 在只能交易一次的情况下,如何求一段时间内的最大利润?其实就是找股价最低的一天买进,然后在股价最高的一天卖 ...