传智播客旗下品牌:  黑马程序员  |  博学谷  |  传智专修学院

改变中国IT教育,我们正在行动     全国咨询热线:400-618-4000

前端培训之浅谈框架模式(MVC MVP MVVM)(一)

更新时间:2016年12月06日11时50分 来源:传智播客web前端培训学院

前言:框架模式不是一门写代码的学问,而是一门管理与组织代码的学问。其本质是一种软件开发的模型。与设计模式不同,设计模式是在解决一类问题时总结抽象出的公共方法(工厂模式,适配器模式,单例模式,观察者模式 。。。 。。。),他们与某种具体的技术栈无关。一种框架模式往往使用了多种设计模式,切不要把他们的关系搞混。
更多信息可以看看这本《Developing Backbone Application》

一代目: 脚本式设计(无架构设计):


下面这样的代码,就是无任何设计模式的产物:
JavaScript

1
2
3
4
5
6
7
8
9
const a = document.createElement("a");
a.innerHTML = "www.google.com";
a.href = "//www.google.com";
a.style.position = "absolute";
a.style.top = 100;
a.onclick = function(){
    console.log("google");
}
 document.body.appendChild(a);
 
嗯嗯~先别吐槽,因为这种编码方式还是有他的优点的。
短短的几行代码,包含了创建,样式,绑定,插入。 balabala。。。。。
这种搞法虽有不少缺点,但麻雀虽小五脏俱全,所有功能一应俱全。早些年由于UI程序还处在一个懵懂期, 逻辑不算太复杂,代码量也不会太多。这样的搞法似乎也没有什么问题。 毕竟到达A B两点最短的距离就是直线,上述代码可以说是实现某功能的最短路径。 典型的例子就是 ASM (虽然汇编语言不是用来写UI的),他们共有的缺点是 :入口单一 功能简单 不可维护
让我们修改一下上面的逻辑:
JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function doCss(a) {
    a.style.position = "absolute";
    a.style.top = 100;
}
function doEvent(a) {
    a.onclick = function(){
        alert("google");
    }
}
function doAttribute(a){
    a.innerHTML = "www.google.com";
    a.href = "//www.google.com";
}
const a = document.createElement("a");
doCss(a);
doEvent(a);
doAttribute(a)
document.body.appendChild(a);
 
这里我们将一个功能拆分成了3个部分,即外观 事件 和属性。
函数将他们重新分离成一个个独立的逻辑块,这样一定程度上达到了分离复用的目的,比如你想修改外观,就去doCss函数里去找。。。
就像有钱人追求更多的财富,权贵追求更多权利一样。
人类总在思考同一个问题, 我们能不能做得更好。。
直到有一天。。。

二代目: 代码文件分离(CodeBehind):


对asp。net尤其是webForm,winForm模式熟悉的同学,肯定对aspx 和 aspx。cs 这2种文件非常了解。
    
aspx是视图文件,而对应的。cs文件是他的相关逻辑处理文件 事件驱动模式下,框架帮我们完成了基本的事件类型,我们要做的是在事件下完成相关业务逻辑。
在前端中,也可以找到二代目大人的影分身。。
JavaScript

1
2
3
4
5
6
7
8
<html>
<head>
    <link href="style.css" rel="stylesheet" />
</head>
<body>
    <script src="bundle.js" ></script>
</body>
</html>
 
各文件各司其职,编写的时候分离,在运行的时候合并。这样进一步降低了功能之间的耦合度。 视图看起来非常”清爽”,对应的逻辑也被分离成一个个文件, 交由相应的开发人员处理。
如果你涉猎的技术范围很广,你会发现其实二代目大人已经出现在诸多成熟的技术栈中。。。。
但是,没过多久伊甸园欢乐的笑声被下面这个需求打破。。。

为了实现他,在这里我只写伪代码了:

1
2
3
4
5
6
7
8
9
10
11
`购买苹果按钮`绑定事件如下:
1.int 苹果数变量 + 1;
2.显示苹果数控件的值 = 苹果数变量;
 
`购买梨按钮`绑定事件如下:
1.int 梨数变量 + 1;
2.显示梨数控件的值 = 梨数变量;
 
...`吃苹果`
 
...`吃梨`
 
这里比较困难的是 第2步
就是如果显示苹果数的控件是另一个程序员开发的黑盒, 如何修改其值?
于是程序员A 去找 程序员B 寻求是否存在对应的 get/set 方法。
程序员B说, “有” 字还没落地, 开发买梨的程序员C 又踹门进来了,问了同样的问题, 后来才知道 开发吃苹果功能的程序员D正在路上。。。
于是程序员B 不得不把接口的详细信息写到 wiki中, 于是 程序员CDEFGHIJKMLN 都看了wiki 懂了。
完成这件事 程序员B 写wiki 花了 10分钟, 程序员CDEFGHIJKMLN 看wiki每人花 1分钟,一共团队成本 20分钟。
于是 wiki中这个 get/set 接口函数出现在了每一个被绑定的函数里。一共4个button 出现4次
ps: 这个get/set接口本质上就是一个view刷新接口
事情仍在在酝酿:
产品大爷发话了,要改成下面这样:

多了一个求和。
于是伪代码变成了这样:

1
2
3
4
5
`购买苹果按钮`绑定事件如下:
1.int 苹果数变量 + 1;
2.int 总和变量 = 苹果数变量 + 梨数变量;
2.显示苹果数控件的值 = 苹果数变量;
3.显示总和控件的值 = 总和变量;

这次 程序员B为了人身安全,提前把 get/set 接口发布到了 wiki上。。
于是 总和控件的值 = 总和变量 这行代码出现了 4次。
产品存在的意义,就是将程序狗虐到极致:
于是需求改成了这样:

只是删了一行。
于是 4个函数中所有相关代码都被删除。。。 共影响 4行代码。。
段子讲完了,其核心问题在于,按照事件进行的业务模块划分,有时候是不合理的,事件是用户行为的入口,但不是程序逻辑的入口。 一个button的click就可能横跨N个领域, 需要N个人来进行协作, 这部分逻辑到最后还是会耦合在一起,通过各种函数封装进行解耦,无疑是扬汤止沸,而我们需要的是釜底抽薪
(第一节结束)

本文版权归传智播客web前端开发学院所有,欢迎转载,转载请注明作者出处,谢谢!
作者:传智播客web前端培训学院;
首发:http://www.itcast.cn/web/