架构模式:MVC 与 MVVM
- 什么是 MVC
- 什么是 MVVM
- MVC 与 MVVM 对架构属性的影响
- MVC 实例 SpringMVC
- MVVM 实例 Vue
- MVC、MVVM 与 Layer 中的 Model,Controller 有什么区别?
MVC 与 MVVM
在「什么是架构模式和架构风格」一文中,对架构模式的定义是:
Architecture Pattern: {Problem, Context} → architecture approach;
架构模式描述了一组组件之间的关系,用以解决特定上下文内的某个常见的架构问题
MVC 和 MVVM 都是架构模式!
- MVC描述了「Model,View,Controller」三者之间的关系,用以解决「有展示界面的系统」「界面开发与业务逻辑的耦合问题」
- MVVM描述了 [Model,View,ViewModel( 和 Binder)」三者之间的关系,用以解决「有展示界面的系统」「界面开发与业务逻辑的耦合问题」
可以看出 MVC 和 MVVM 都是为了解耦「界面」和「业务逻辑」,只是解决方案不同!也可以说 MVVM 是 MVC 的一种变体 (ViewModel+Binder 可以说是 Controller 的一种实现方式),或者衍生!
我们先说,为什么要解耦「界面」和「业务逻辑」?
早期的界面系统开发时,展示逻辑与数据是糅合在一起的!以早期的 Java 为例,所有的展示逻辑和业务逻辑都写在了 Servlet/JSP 中,导致了:
- 单个文件的代码量较多,既包含了展示代码又包含了业务逻辑
- 违背了职责单一性原则
- 修改麻烦
- 复用性差
- 不易于维护
- ……
MVC 将系统拆分为控制器、视图和模型来解决上面的问题:
- 控制器(Controller)- 负责转发请求,对请求进行处理。
- 视图(View)- 界面设计人员进行图形界面设计。
- 模型(Model)- 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计 (可以实现具体的功能)。
Wiki 给出了一个 Js 模拟的 MVC 模式代码,能更清晰的理解三者之间的关系:
/** 模拟 Model, View, Controller */var M = {}, V = {}, C = {};/** Model 负责存放数据 */M.data = "hello world";/** View 负责将资料展示出来 */V.render = (M) => { alert(M.data); }/** Controller 作为M和V的桥梁 */C.handleOnload = () => { V.render(M); }/** 在页面加载的时候调用Controller*/window.onload = C.handleOnload;
这样的拆分,提高了系统的:
- 可扩展性:View、Model 和 Controller 分别负责视图、数据和控制,可独立进化。能较方便的增加新功能。
- 可维护性:结构清晰,多个 View 可以复用一个 Model,减少了代码重复
- 可测试性:每个组件的职责单一,可以对 Model 进行自动化测试
- 灵活性:Controller 可以用来连接不同的 Model 和 View 去完成用户的需求
- 可配置性:给定一些可重用的 Model 、 View 和 Controller 可以根据用户的需求选择适当的 Model 进行处理,然后选择适当的的 View 来处理结果显示给用户
而 MVVM 模式将系统拆分为视图、模型和视图模型以及绑定器来解决耦合问题:
- Model:Model 是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。和 MVC 中的 Model 指代的内容相似。
- View:就像在 MVC 中一样,View 是用户在屏幕上看到的结构、布局和外观(UI)。
- ViewModel:ViewModel 是暴露公共属性和命令的视图的抽象。
- Binder:绑定器是 MVVM 模式里的一个隐含组件,ViewModel 中声明了数据与 View 之间的绑定关系,而绑定器来处理声明的绑定关系。
ViewModel 和 Binder 的关系就像 Java 里的注解与注解处理器之间的关系:注解只是标示了某个字段、方法或某个类应该具备什么属性;注解处理器根据注解,对被注解的字段、方法或某个类来进行实际的处理。
这样的拆分,与 MVC 模式一样,提高了相同的系统属性,只是方式有一些差异:
- 可扩展性:View、Model 和 ViewModel(和 Binder) 分别负责视图、数据和数据视图绑定,可独立进化
- 可维护性:结构清晰,多个 View 可以复用一个 Model,减少了代码重复
- 可测试性:每个组件的职责单一,可以对 Model 进行自动化测试。前台也可以对 ViewModel 进行自动化测试。
- 灵活性:ViewModel 可以用来绑定不同的 Model 和 View 去完成用户的需求
- 可配置性:给定一些可重用的 Model 、 View 可以根据用户的需求选择适当的 Model 进行处理,然后选择适当的的 View 来处理结果显示给用户
SpringMVC
SpringMVC 是目前使用比较广泛的 MVC 框架!它是 MVC 与「前端控制器」的混合体!
前端控制器模式(Front Controller Pattern)是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证 / 授权 / 记录日志,或者跟踪请求,然后把请求传给相应的处理程序。前端控制器包含如下组件:
前端控制器(Front Controller):处理应用程序所有类型请求的单个处理程序,应用程序可以是基于 web 的应用程序,也可以是基于桌面的应用程序。
调度器(Dispatcher):前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。
视图(View):视图是为请求而创建的对象。
在 SpringMVC 中:
- View:指的是各种展现模板,比如:velocity,freemark,theamleaf 等
- Controller:指的是前端控制器与 Controller 层
- Model:指的是 Controller 后的后续处理组件,比如 Service,Model,Domain,DAO 层等
SpringMVC 的前端控制器做了很多事情,结构如下图:
- HandlerMappingMap: 维护了请求与处理程序 (一组前置拦截器 + 处理程序 + 后置拦截器) 之间的关系,如何维护映射关系,由具体的实现决定。例如:RequestMappingHandlerMapping 基于 @RequestMapping 注解来维护映射关系;SimpleUrlHandlerMapping 根据 URI 匹配关系来处理映射关系
- HandlerAdapter:用于执行具体的处理程序。由于不同的映射关系,执行处理程序的方式也不一样,HandlerAdapter 封装了这些执行差异
- HandlerException:异常处理程序,将异常映射到处理程序、或 HTML 或其它组件上。即可以通过配置,统一的处理某一种类型的异常
- ViewResolver:通过解析「基于字符串的视图名称」来找到真实的视图,来进行渲染,回写到响应中
- LocaleResolver, LocaleContextResolver:处理国际化
- ThemeResolver:处理主题
- MultipartResolver:处理 multi-part 请求 (上传文件)
- FlashMapManager:实现 Flash 作用域。因为标准 J2EE 中只提供了 page,request,session,application 四种作用域。Spring 通过 FlashMapManager 实现了 Flash 作用域,用于在单个请求内传递参数。
Vue
Vue 实现的是 MVVM 模式!
在 Vue 中:
- View:指的是各种 template,即基于 html 语法的展示
- ViewModel:指的是对应的 js,声明绑定的元素及绑定的数据
- Binder:处理 template 与 js 的绑定逻辑
- Model:指的是获取数据的逻辑。如果使用的是 node,则就是 node 相关逻辑代码;如果调用的是远程服务,则指的是远程服务
一个简单的 Vue 代码如下:
{{content}}
下图是 Vue 的生命周期:
- Vue 对象就是 ViewModel,它声明了:
- 要处理哪个 dom「el」
- 有哪些数据「data」
- 有哪些方法「methods」
- 生命周期钩子(beforeCreate,created…)
- Binder(Vue 框架处理代码)根据上面的声明,来进行相应的处理
此 Model 非彼 Model
在上面有三处提到了 Model 和 Controller,这三处的 Model 和 Controller 并不完全相同!
三处 Model:
- MVC 中的 Model:这里的 Mode 指的是提供数据相关服务的组件。或者说是业务处理逻辑。
- MVVM 中的 Model:和 MVC 一致
- 系统中的 Model:指的是分层系统中的 Model 层(代码分层),主要作为系统与持久层的数据载体,可以是只有 get、set 方法的 POJO 也可以是包含领域方法的领域对象
三处 Controller:
- MVC 中的 Controller:指的是连接 View 和 Model 的组件
- Spring 中的前端控制器:Controller 的一种实现,统一处理请求
- Spring 中的 Controller:分层系统里的 Controller 层(代码分层,可能叫 Handler 更合适),属于 MVC 中的 Controller