我理解的MVC
前言
前一阶段对MVC模式及其衍生模式做了一番比较深入的研究和实践,这篇文章也算是一个阶段性的回顾和总结。
经典MVC模式
经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。但对于Model和Controller的定义则较为模糊,以致在项目实践中对它们的职责产生了很多不同的理解。其中比较主流的有下面两种。
1、闭环党
比较传统,问题是Model和Controller职责不清,在实操时容易走形
2、 开放派
MVP的前身,问题是Controller职责太重,优点是View和Model没有了直接的关联
对MVP的一点浅见
如果我们希望View和Model脱离关联的话,那么很容易就会使得所有的职能都落到Controller头上,就如同上图所示。这样,Controller也就变成了Presenter,MVC也正式演化为MVP。所有的数据都由Presenter来驱动,所有的业务逻辑也由Presenter来实现。
MVP模式常见于Android,是谷歌官方推荐的App设计模式。从我找到的这张图上,可以非常明显地看出三者之间的关系。
Model只是一个数据通道,退化成了Repository。如果将Model扩而大之,那么就会变成下面这张图描述的场景:
Model摇身一变,成了一个数据交换中心。
MVP模式的优点是实现了View和Model的解耦,缺点是Presenter职责太重。
对MVVM的一些理解
MVVM模式现在非常流行,下面这张图描述了MVVM模式各部分的关系和职能:
MVVM模式同样实现了View和Model的解耦。不过和MVP模式不同的是,MVVM是从数据驱动的角度出发来解决这个问题的。ViewModel和View实现双向数据绑定,ViewModel的数据变化自动地反应到View上。这样,ViewModel就代表了View,成了一个Agent。ViewModel也承担一点业务逻辑,但非常有限(也有把大量业务逻辑放在ViewModel里面的做法,这个就和MVP没什么本质区别了)。所以,业务逻辑的职能就只能由Model来扛了。事实上,MVVM模式本质上是MVC模式去掉了Controller或者说是Model合并了Controller。
MVVM模式的优点是双向数据绑定带来的便利,Model完全不需要关心View。以数据为核心的视角非常新颖,并且在处理业务逻辑上也更加直观。缺点其实和MVP一样,只不过它是Model臃肿而已。
我们为什么需要MVC?
MVC、MVP、MVVM(这里把它们统称为MVC),这种模式的真正好处是什么?说实话,这个问题好思考了很久,脑海里闪过的答案总觉得似乎还差那么一点。最终,梳理出以下几点,和大家探讨:
专业分工的需要
程序员和设计师的技能是完全不同的,用户界面就应该由设计师来完成而非程序员。所以,我们需要一个不包含任何逻辑代码的View,以便由设计师来完成用户界面的创建工作。程序员则可以专心去做和逻辑、数据相关的工作。在这个层面上,不需要考虑让人糟心的Model/Controller还是Model/Presenter或者Model/ViewModel如何划分职责的问题。View的分离显然非常成功!
应对变化的需要
既然用户界面的问题已经得到了完美解决,那么,就该轮到业务逻辑和数据处理的问题了。需求总是在不断变化,程序猿对于产品狗的敌意就来自于不断地更改需求。于是,少改、好改就成了程序员的永恒目标。行之有效的套路其实也就是分层解耦而已。无论是Model/Controller还是Model/Presenter或者Model/ViewModel,不过是分层的角度和方法不同而已。
流程工艺的需要
在软件工程领域,规范提得很多,流程提得很少,工艺几乎没有人提过。但在传统的制造业和工程施工领域,最核心的就是流程和工艺。流程和工艺,是工业化生产的组织和产品品质的基础。
在软件工程上,代码风格规范就是一种工艺,瀑布式开发或者敏捷开发都是流程。如何划分Model和Controller的职责,是软件的一个设计过程,也属于工艺的范畴。三者之间如何依赖,其本质是一个流程问题。被依赖的一定是先于依赖者被生产出来。明确了职责划分和依赖关系,才能科学地编制开发计划,保证产出的代码的品质和效率。
有『套路』可循,我们做起事情来总是会简单快捷许多。
传统MVC模式存在的问题
我们知道,经典MVC模式早先的主要问题是Model和Controller的职责不明,但现在,主要问题是无法进行单元测试。既然已经知道问题在哪里,那么,我们来想办法解决问题就好了,但这之前,还需要解决几个别的问题。
业务逻辑、界面逻辑和数据逻辑傻傻分不清
从广义上讲,无论是点击按钮打开一个对话框,还是拨动一个开关切换界面样式,或者验证输入数据的合法性,都是业务逻辑,并没有什么必要分得那么细。从某种意义上,把业务逻辑分为内在的、直接的反应,和需要用户进一步操作的,状态不确定的过程,可能更加有用。前者例如分页显示的列表用户点击了下一页按钮,从而产生了刷新数据的指令;后者例如用户点击了编辑按钮,是否会修改数据,修改成什么数据在这个时候都是未知的。
三者之间如何依赖
这是一个大问题!经典的VMC模式中,View是依赖于Model的。但我个人的理解是View是需求的最直接的体现,所以View应该是先验的存在,应该是Model依赖View而不是相反。在实际的项目中,在确定原型后,设计师和负责接口的程序员会同时开始工作。同时,测试工程师也会开始编写测试用例和单元测试代码。他们的工作依据都是产品给出的原型。
等一个View编写完成后,依赖于这个View的Model就可以开工了,每一个View都会对于着一个Model和若干的接口。最后,就轮到依赖于若干Model的Controller登场了。这样做的好处是整个开发过程是由底向上、由表入里的,整体过程非常自然,而且结构简洁明了。
如何划分Model和Controller的职责
这是一个更大的问题,足以引起开发者的争吵不休,就如同什么语言最好一样。
抛开这些分歧,我们就会看到,无论是什么模式,它所需要解决的无非是业务逻辑和数据问题!所以,我认为问题的本质不是谁负责什么,而是如何分离业务逻辑和数据。
特定的用户界面需要特点的数据,有着特定的业务逻辑。这个前提之下,解耦是没有意义的。我们要求的职责分明,无非是为了更容易应对变化。那么,都有哪些变化呢?
- 界面样式变了,但业务逻辑和数据都没有变
- 界面样式和业务逻辑变了,但数据没有变
- 界面和数据变了,但业务逻辑没有变
- 界面和数据没有变,但业务逻辑变了
- 全都变了
界面的改变可以分为两种,一是样式改变,这种变化无关其他;另一种是元素变化,必然对应着数据的变化,需要修改接口。另外,就是业务逻辑的改变,而业务逻辑和数据并不存在必然关系。在MVC模式下,View的数据来源于Model,那么,Model的职责就是负责View和接口之间的数据交互,起一个数据通道或者说是数据引擎的作用。当数据发生变化的时候,只需要修改Model即可。
既然Model承担了数据引擎的职责,那业务逻辑就应该由Controller来承担。同时,因为不同的View之间也会存在交互,那么,也需要一共同的个中间人来进行转接和调度,由于Model和View是一对一的绑定关系,并不适合承担这个责任。所以,Controller负责业务逻辑是天然的。不过,那些和数据直接相关的事件,例如改变了一个选项后引起可用数据的变化、切换分页加载新数据之类的和具体业务没有关系的简单反应型的业务逻辑,我觉得交给Model去实现更简单和直接,并不一定要经过Controller去驱动Model。
在这种模式下,Model负责数据,Controller负责业务逻辑,整体是非常协调的。反过来看MVP和MVVM,因为回避职责的划分的问题导致了Presenter或Model的臃肿。
View和Model要不要双向数据绑定
非常有必要!传统的MVC是没有双向绑定的,这样,View上面数据的变化就必须通过Controller去修改Model。而建立双向绑定后,Controller就无需承担这个职责了,从整体上看,职责更加分明,逻辑也会更加简单。
改进的MVC模式
解决了以上四个问题,我们可以得到这样的一个新的MVC模式:
这种改进模式,相对传统的MVC模式,解决了职责不清的问题。相对于MVP和MVVM而言,没有因回避职责划分问题导致的庞大而混乱的Presenter/Model。在仅仅是View样式不同的场景下,Model是可以复用的。而使用哪个View,可以通过重载Model的构造函数来决定。事实上,即使View的元素不同造成数据不同,Model也可以利用泛型等技术手段来达到重用代码的目的。
因为View并不依赖任何人,所以,我们可以很方便地把View替换成单元测试代码(View本身是可根据场景需要相互替换的),只要骗过Model就OK。这个测试类一旦被Model构造出来,就会自动验证数据、模拟用户更新数据和发出指令。
在项目中展开的话,结构如下图:
后记
这不是结束,而是一个开始……
我理解的MVC的更多相关文章
- 【blade的UI设计】理解前端MVC与分层思想
前言 最近校招要来了,很多大三的同学一定按捺不住心中的焦躁,其中有期待也有彷徨,或许更多的是些许担忧,最近在开始疯狂的复习了吧 这里小钗有几点建议给各位: ① 不要看得太重,关心则乱,太紧张反而表现不 ...
- HttpModule的认识与深入理解及MVC运行机制
转自:http://kb.cnblogs.com/page/50130/ ASP.NET MVC架构与实战系列之二:理解MVC路由配置 http://www.cnblogs.com/jyan/arch ...
- 理解Spring MVC Model Attribute和Session Attribute
作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域.在设计和构建 Java Web 应用 ...
- 项目开发设计模式理解之MVC模式
项目开发设计模式之MVC模式: M model 模型层 V view 视图层 C control 控制器 MVC模式在B/S架构下使用很广泛的软件设计模式,分成三个相对独立的模块构成,model+vi ...
- 深入理解Spring MVC(山东数漫江湖)
初始工程 使用Spring Boot和web,thymeleaf的starter来设置初始工程.xml配置如下: <parent> <groupId>org.springf ...
- 【译】理解Spring MVC Model Attribute 和 Session Attribute
作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域.在设计和构建 Java Web 应用 ...
- 深入理解Spring MVC
如何让一个普通类成为Controller? 方案一:实现接口Controller 解析:handleRequest(request,response) 方案二:继承AbstractController ...
- 深入理解Spring MVC 思想
目录 一.前言二.spring mvc 核心类与接口三.spring mvc 核心流程图 四.spring mvc DispatcherServlet说明 五.spring mvc 父子上下文的说明 ...
- 转载:深入理解Spring MVC 思想
原文作者:赵磊 原文地址:http://elf8848.iteye.com/blog/875830 目录 一.前言二.spring mvc 核心类与接口三.spring mvc 核心流程图 四.sp ...
随机推荐
- 分治法求解最近对问题(c++)
#include"stdafx.h" #include<iostream> #include<cmath> #define TRUE 1 #define F ...
- Android业务组件化之现状分析与探讨
前言: 从个人经历来说的话,从事APP开发这么多年来,所接触的APP的体积变得越来越大,业务的也变得越来越复杂,总来来说只有一句话:这是一个APP臃肿的时代!所以为了告别APP臃肿的时代,让我们进入一 ...
- 协议森林17 我和你的悄悄话 (SSL/TLS协议)
作者:Vamei 出处:http://www.cnblogs.com/vamei 转载请先与我联系. TLS名为传输层安全协议(Transport Layer Protocol),这个协议是一套加密的 ...
- .net 大型分布式电子商务架构说明
.net大型分布式电子商务架构说明 背景 构建具备高可用,高扩展性,高性能,能承载高并发,大流量的分布式电子商务平台,支持用户,订单,采购,物流,配送,财务等多个项目的协作,便于后续运营报表,分析,便 ...
- AI人工智能系列随笔:syntaxnet 初探(1)
人工智能是 最近的一个比较火的名词,相信大家对于阿尔法狗都不陌生吧?其实我对人工智能以前也是非常抵触的,因为我认为机器人会取代人类,成为地球乃至宇宙的霸主,但是人工智能带给我的这种冲击,我个人感觉是欲 ...
- iOS之UILabel的自动换行
思路: 获取UILabel的frame大小 获取UILabel的字体大小 获取UILabel的文本内容 根据上面的3部分数据,计算文本显示区域大小 根据4计算的大小,实时改变UILabel的frame ...
- 【iOS10 SpeechRecognition】语音识别 现说现译的最佳实践
首先想强调一下“语音识别”四个字字面意义上的需求:用户说话然后马上把用户说的话转成文字显示!,这才是开发者真正需要的功能. 做需求之前其实是先谷歌百度一下看有没有造好的轮子直接用,结果真的很呵呵,都是 ...
- 【转】Android开发中让你省时省力的方法、类、接口
转载 http://www.toutiao.com/i6362292864885457410/?tt_from=mobile_qq&utm_campaign=client_share& ...
- 图解Spark API
初识spark,需要对其API有熟悉的了解才能方便开发上层应用.本文用图形的方式直观表达相关API的工作特点,并提供了解新的API接口使用的方法.例子代码全部使用python实现. 1. 数据源准备 ...
- Ubuntu 16.04 安装 arm-linux-gcc 嵌入式交叉编译环境 问题汇总
闲扯: 实习了将近半年一直在做硬件以及底层的驱动,最近要找工作了发现了对linux普遍要求很高,而且工作岗位也非常多,所以最近一些时间在时不时地接触linux. 正文:(我一时兴起开始写博客,准备不充 ...