沙龙活动 | 3 月 31 日 京东微博华为实战专家与你共同探讨容器技术实践!
前言: 准备写这篇文章的时候 , 我自认为对 MVC 已经有深刻理解了, 可是画图的时候发现, 理解还是有漏洞, 于是又阅读, 思考, 整理, 加深了理解, 写了这篇文章, 估计还有漏洞, 欢迎讨论
这再一次说明了写作的好处: 很多时候自以为理解了, 实际上脑海中有很多想当然的假设, 写作会把这些假设给暴露出来
大概是二三十年前, 人类逐渐从命令行界面时代走出来, 进化到了 GUI 时代
注: GUI(Graphic User Interface), 即图形用户接口
(一个命令行程序)
(一个带有图形界面的桌面应用程序 , 自己画的, 有点丑啊)
每当人类努力地开发新的桌面 GUI 程序的时候, 至少要搞定下面几类工作:
1. 界面 (以及界面中元素的) 布局
这是一件挺费劲的工作, 要尽可能地美观漂亮, 要不然就卖不出去
2. 界面上有些逻辑需要处理
比如上图中那个薪水计算程序, 计算 按钮默认是灰色的, 不能点击, 用户输入了税前收入以后, 计算按钮就会被激活, 表示计算了
3. 所谓的业务逻辑
用户点击了计算按钮以后, 计算五险一金, 个人所得税和税后收入
这三者搅在一起, 让程序代码凌乱不堪, 稍微复杂点儿的程序就长达几千行, 不断地挑战着程序员的底线, 修改别人的代码, 添加新的功能要比从头写难好多倍!
大家都在泥潭中挣扎
桌面应用程序的 MVC
程序越来越复杂, Bug 越来越多, 没办法, 大家只好去求编程上帝
上帝说: 想从困境中走出来, 一定要实现关注点分离(Separation of concerns)
没人能够理解
上帝解释道: 你们人脑同时能处理的东西是有限的, 所以要把一个大系统给分解, 变成几个相对独立的部分, 这样我们的大脑每次只关注某一方面, 暂时忽略其他的, 就能够掌控了
没人知道该怎么分解
上帝只好想了一个办法, 把关注点分离的理论给具体化, 这个办法就是 MVC
上帝告诉人类:
M 表示 Model , 专门用来处理业务逻辑, 不干别的事情
例如在那个薪水计算系统中计算一个人的薪水, 五险一金, 个人所得税等等
V 表示 View, 专注页面布局和数据显示
例如把 Button 放置到某个位置, 把总收入显示到一个文本框, 把税金显示到另外一个地方
C 表示 Controller 翻译用户的输入, 操作模型和视图
例如, 用户在界面点击了一个计算的按钮, View 把计算的请求传递给 Controller (很明显 View 需要知道 Controller, 换句话说, 需要持有 Controller 的实例),Controller 找到或者创建 Model, 执行业务逻辑: 计算薪水
计算的结果该怎么展示呢? 人类问道
上帝胸有成竹: 可以让 Model 去通知 View
Model 需要持有 View 的实例(当然也可以通过观察者模式), 调用 View 对应的方法
例如: View 中可能有一个 onResult 的方法, 让 Model 去调用, 在调用的时候把一个参数对象 Salary 传递过来, 不就可以展示数据了吗?
- // View 的方法, 被 Model 调用:
- public void onResult(Salary salary){
- // 把个人所得税(salary.getTax()) 展示到一个文本框
- // 把净收入(salary.getNetPay()) 展示到另外一个文本框
- ......
- }
画成流程图的话是这个样子:
大家都觉得 MVC 大法好, 纷纷开始使用
MVP
时间久了以后, 人类就觉得不爽了, 因为在这个 MVC 中, 依赖太多:
View 依赖 Controller 和 Model
Controller 依赖 View 和 Model
Model 和 View 的关系虽然很弱, 但是也需要某种方式来通知 View 进行数据更新
人类说: 他们之间的耦合还是挺紧密的啊, 亲爱的上帝, 能不能改改?
上帝觉的人类还是挺有上进心的, 决定继续施以援手: 这样吧, 可以改变一下 Controller, 把 Model 和 View 完全隔离开, 让他们单独变化
上帝把 Controller 改了个名称, 叫做 Presenter, 把整体命名为 MVP
在 MVP 当中, View 只知道 Presenter, 不知道 Model
计算流程和 MVC 差不多, 用户点击了计算薪水按钮, View 去调用 Presenter, Presenter 操作 Model , Model 中进行业务计算 关键点是, Presenter 去更新 View
- //Presenter 的方法, 被 View 调用
- public void calculateSalary(){
- // 调用 Model 计算薪水
- view.showTax(xxx); // 调用 View 显示所得税
- view.showNetPay(xxxx);// 调用 View 净收入
- ......
- }
但是 Presenter 还是需要调用 View 的方法, 也就是说 Presenter 对 View 有依赖, 这样 Presenter 就没办法单独做单元测试, 非得等到界面做好以后才行
于是上帝又做了一点改进, 让 View 层提取出接口, Presenter 只依赖这个接口
这样 Presenter 不用依赖真正的界面就可以测试了, 并且也增加了复用性, 只要 View 实现了那个接口, Presenter 就可以大发神威
MVVM
使用了一段时间 MVP 以后, 永不满足的人类又觉得不爽了, 因为让 Presenter 调用 View 的方法去设置界面, 仍然需要大量的烦人的代码, 这实在是一件不舒服的事情
人类突发奇想: 能不能告诉 View 一个数据结构, 然后 View 就能根据这个数据结构的变化而自动随之变化呢?
上帝看到人类思考了, 表示了赞赏
他说, 我来送你们一个叫做 ViewModel 的东西, 它可以和 View 层绑定 ViewModel 的变化, View 立刻就会变化
人类问: ViewModel? 里边有什么东西?
上帝说: 拿你们的薪水计算为例, ViewModel 差不多这样:
- public class SalaryViewModel{
- String grossSalary; // 税前收入, 和 View 中的相关字段对应
- String netSalary; // 净收入, 和 View 中的相关字段对应
- String tax; // 个人所得税, 和 View 中的相关字段对应
- ......
- boolean isCalculating; // 一个标志位, 表示正在计算
- String errMsg; // 如果出错的话, 记录出错消息
- }
当用户在界面上点击计算按钮的时候, 你们需要设置一个 SalaryViewModel 中的标志位:
salaryViewModel.isCalculating = true;
这样 View 中就可以自动给用户展示一个消息: 正在计算....
当薪水计算完成的时候, 如果没有错误, SalaryViewModel 中 grossSalary, netSalary,tax 等属性就有了值 与此同时 View 中对应的内容也会更新, 不用你们手工去设置, 很方便吧?
如果计算过程出错, SalaryViewModel 的 errMsg 会保存出错消息, 同样, View 中会自动把这个错误消息给显示出来, 很智能吧?
人类说: 怎么可能这么智能呢? 这里的 ViewModel 好像和 View 没有什么关系啊? 到底该怎么绑定啊?!!!
上帝笑了: 你们可以开发一个框架嘛? 让两者绑定起来不就行了?
人类没有办法, 只好自己动手
(注: 实际上微软的 WPF 和 Silverlight, Android 等框架和系统都可以实现 View 和 ViewModel 之间的映射和绑定)
web 应用程序的 MVC
时间过得飞快, 人类发明了互联网, Web 应用程序如雨后春笋般崛起, B/S(浏览器 - 服务器)开始大行其道
用户通过浏览器发出 GET,POST 请求, 服务器端进行处理, 处理完以后生成 html 给浏览器
无论什么操作, 都是对服务器端 URL 的访问
人类突然发现, 整个编程模型发生了巨变, 不能简单地套用原来的 MVC 和 MVP 了
如果把 HTML 页面比作原来桌面应用程序的 View, 服务器无论是 Controller 还是 Model 都是无法远程遥控这个 View 进行处理的
人类这一次没有去请教上帝, 自己尝试对 MVC 进行改良, 其中有个叫 Rod Johnson 带领一帮人搞出的 SpringMVC 很成功
不像桌面应用的 MVC, 这里的 Model 没法给 View 发通知
也不像 MVP, 这里的 Controller 也不会调用 View 的方法来设置界面
实际上 Controller 会选择一个 View, 然后把模型数据丢过去渲染
原来的 View 变成了 View Template(例如 JSP , Velocity 等等), 经过渲染后变成 HTML 发给浏览器展示给用户
有人把这种 MVC 称为 基于 Web 的 MVC, 以便和之前的 MVC 区别开来
前后端的分离
人类早期的 B/S 应用程序中, 每次访问服务器端, HTML 就会整体发给浏览器, 即所谓的整体刷新
后来人类发明了 AJAX, 可以做到局部刷新
于是浏览器端的应用变得越来越复杂, 再后来人类竟然发明了 Web 上的 SPA(单页应用程序), 用起来的体验和最初的桌面应用程序越来越像
人类发现, 那些 MVC, MVVM 之类的模式完全可以用到浏览器端嘛!
例如在浏览器端使用 MVVM , 在服务器端可以使用 MVC, 两者结合起来:
前端和后端成功地分家了 !
来源: http://zhuanlan.51cto.com/art/201803/568600.htm