刚过去的周五(3-14)例行地主持了技术会议,主题正好是《UI层的设计模式——从Script、Code Behind到MVC、MVP、MVVM》,是前一天晚上才定的,中午花了半小时准备了下就开讲了。javascript
今天看到了你们在为MVVM knockout.js友(ji)好(lie)地交流,因此就整理下而后更扩展地分享。css
主要目的也不是为了争论,毕竟只是正巧主题相近,本来的打算也就是一次技术分享而且记录下来。html
那么咱们就按照大体的历史进程将这些概念进行划分:前端
咱们知道的是现实的历史发生顺序并不如上,由于思想都是类似的,好比MVC很早很早就出现了,解释型语言至今基本上也有不少分支并且在互联网时代大行其道。java
但我要说的是:不要在乎这些细节!算法
固然了,这是玩笑,个人意思是,这些内容我懒得应该在另外独立的主题探讨,篇幅也有限。设计模式
这里脚本的意思不是指当时是用脚本开发,而是像写脚本同样开发。它们都有一个特色:功能单1、管理单1、入口单一。浏览器
咱们最先的程序是汇编,当时的码农的工做是兼职,工做内容是编写一套寿命不长的机器控制指令,有些甚至是命令,好比至今依然保留的Command(亮点自寻):网络
到后来计算机被用于科学计算等,需求推进了须要更高的开发效率,因此咱们有了高级语言。架构
那个时候码农其实可能是数学家,程序的做用很简单,就是执行一些数学计算,相似今天ICPC的一些算法问题,好比Hello World:
main() { printf("hello,world\n"); }
这时候,程序还能够被归结为输入到输出的过程,咱们还能去讲讲冯诺依曼模型。
在这个时代,开发是指编写机器指令,甚至都不配用“开发”这个词来描述这项工做。
在那个时代讲UI等于放屁,根本不存在这种概念。但全赖咱们有神器——摩尔定律。
但我我的认为摩尔定律是不足以让一个敲命令行的时代在几十年间转变成这种各种框架技术架构实践模式的时代,真正推进计算机造成自有的工程学体系的是还有两样东西就是:
由于人的能力并无“跟上”机器,因此才会出现各类模式、方法、工具等等来补足人的不足,以最大地透支机器性能。就像我前几天在闪存无聊时忽然想到的一句: 架构是对客观不足的妥协,规范是对主观不足的妥协。
当咱们须要机器作的事情多了起来,咱们就没办法在一个芯片上解决全部事情,因此才会有冯诺依曼模型、计算机架构,没办法用一台机器解决,因此才要互联网、分布式、云计算。
一样,随着计算机的发展,要作的事情多了,就出现了软件的概念。当“开发”正式化,咱们需求的软件就变得:功能繁杂、管理统1、多入口。
真正变化的不是客观本质,而是需求。就像这里说的“软件入口”在客观上咱们仍是只有一个,原理上始终都只有一个启动程序、一个启动代码片断。但“软件的入口”,已经从指代Main函数变成了指代起始UI,用户已经从指代专业人士变成了指代通常消费者,先有软件的需求,才有软件的定义,而需求是在变化的。
一个软件须要比当时多几个数量级的代码:
因此咱们须要添加索引、用多个文件组织代码。
机器的发展和软件的需求扩大和细化,咱们开始出现了用户界面(User Interface)的概念和最适合用于界面的语言——标记语言(Markup Language)。固然,ML不是为UI而生的,它只是十分适合UI,因此才和UI坠入爱河。
由于有了更高UI的需求,因此代码才正式被分化为描述作什么(业务逻辑)和有什么(UI)的两部分,由于咱们开发时没办法在两种思惟方式下同时工做,开发时的人脑是单线程的。咱们所看到的同时进行UI和逻辑开发只不过是咱们学会了在两种模式下快速切换,看起来像同时进行,而不是真正的同时进行。一样的状况也发生在不一样的代码片断的开发中。
分化的状况除了UI,还发生在方方面面,好比数据操做、UI的对象和样式分离,咱们仍是继续从UI讲下去吧。
UI和逻辑分开了两种语言来写,可是它们也要放在同一个项目中,由于它们本来就是要一块儿工做的。即便是分开,也须要相连,由于这是它们原本要解决的问题。
这时候咱们出现的(一般的)解决方案就是Code Block和Code Behind。虽然从时间上彷佛Code Block比Code Behind要早,有种感受就是越新越好,但实质上它们正交替地发展着,由于谁也没办法解决UI和逻辑代码分化后的一个哲学问题——UI和逻辑是一块儿的,可是它们却不是一块儿的。
Code Block能很好地处理UI和逻辑间在一块儿的关系,你在同一个地方能够同时维护UI和逻辑:
1 @model GM.OA.Finance2.Web.Models.FinancialBase.CurrencyModel 2 @{ 3 ViewBag.Title = "EditCurrencyDrawer"; 4 Layout = "~/Views/Shared/_DrawerLayout.cshtml"; 5 } 6 7 @section styles { 8 <link href="/Content/base/table-form.css" rel="stylesheet" /> 9 <link href="/Content/base/drawer.bigtitle.css" rel="stylesheet" /> 10 }
<a href="#" class="addcurrency oa-btn" oa-style="green">添加新币别</a> <script type="text/javascript"> $(document).ready(function () { $('.addcurrency').click(function () { $.oa.drawer.openUrl('AddCurrencyDrawer/', 'addcurrency', { width: 300 }); }); }); </script>
Code Behind能很好地处理UI和逻辑各自分开的关系,你可让UI和逻辑各自作好各自的事情:
<asp:Button ID="RemoveSelectedCurrency_Button" runat="server" Text="删除选中币别" OnClick="RemoveSelectedCurrency_Button_Click" /> <asp:Button ID="RemoveAllCurrencies_Button" runat="server" Text="删除全部币别" OnClick="RemoveAllCurrencies_Button_Click" />
1 protected void RemoveSelectedCurrency_Button_Click(object sender, EventArgs e) 2 { 3 var currencyId = Currencies_ListBox.SelectedItem.Value; 4 currencyManager.Remove(currencyId); 5 } 6 7 protected void RemoveAllCurrencies_Button_Click(object sender, EventArgs e) 8 { 9 currencyManager.RemoveAll(); 10 }
由于存在两种实现方式,因此就存在了对比,由于存在了对比,因此就存在了争论。就像是Java和.NET、PHP和.NET、WebForm和MVC、Mac OS和Windows、iOS和Android、腾讯和全部其余互联网公司,等等。
问题不在哪一个更好,而是咱们要解决什么问题。固然,这听(ben)着(lai)像(jiu)是客气话了。
真正在UI和逻辑分化后带来的实质问题是:
至少UI和逻辑刚分化的时代,在软件上,咱们还认为同一项事务是基于同一个UI的一系列操做完成的。
在摩尔定律还持续发挥做用的时候,计算机领域依旧高速发展,因此一般咱们还在为同样事物争论、思考、辩证的时候,它已经发生了质变了,不变的只是咱们要解决的问题。
在以前说到过了,当需求变得庞大,解决方案也会变得庞大;当解决方案变得庞大,就会出现细分;当出现细分,就会出现按哪一种方式管理的问题。
软件从处理一件事务发展到了要处理许多事务,各事务间有包含、顺序、主次等等的关系,变得愈来愈复杂。由于数据与逻辑庞大了,因此数据与逻辑就分离了,而后事件和业务分离了。
它们的关系已经在咱们还理得清以前持续发展而变得更加难理得清,但在一个时间点上,它们UI的领域大体分化成这些原子:
你要细化的话会有更多繁杂的细节,但相信这么写的话争议性比较小。
当一个问题出现一次的时候它是一个问题,当一个问题出现了无数次的时候它会成为历史,当一个问题将会出现无数次的时候,它将须要一个明确的定义和解决方案。
其中,数据的更新和界面的更新这一特殊事件的问题被放大了无数倍,由于它出现了无数次。
在ASP还在奋斗的时候WebForm忽然到来,正如WebForm还在奋斗的时候MVC忽然到来。固然,我这里讲的MVC仍是最原始的MVC,由于MVC在咱们还在争论的时候已经发展了许多不一样分支了。
有一点相信你们赞成的就是,咱们今天讨论争论的MVC、MVP、MVVM、Code Behind等等都源自于职能分化和规划的思想与目的,MVC不是它们的开始,可是一个很好的开始。
相信MVC的模型你们很熟悉,也很容易找到,咱们这里用一下某百科的图:
咱们能够看到的是,界面被分到了View,数据分到了载体Model上由Model“携带”,业务集中在Controller中,而推进业务的事件由用户与View交互,经过View向Controller发动。
固然,实现由不少种,每种细节上都有不一样,因此我才只讲也只能讲大体的MVC。MVC的其中一个缺点即是没有明确的定义,因此不一样的实现(好比Struts和ASP.NET MVC)细节上都是不同的。
咱们须要知道的是,MVC并非像上面所说的一些事情那样是一种“必然的”结果,它是一系列必然结果问题中的一种解决方案,并且是不完美的解决方案。咱们顺着推理去到一个地方很容易犯的一个错误就是认为路只有这一条而忽视其余可能性(估计这也是致使不少争斗的缘由)。另外,咱们在讨论一件事物不完美的时候是有一个情境的,因此请不要像“我说它色彩单一,而后你把它涂成彩色后证实我是错的”。
MVC的通常流程是这样的:View(界面)触发事件--》Controller(业务)处理了业务,而后触发了数据更新--》不知道谁更新了Model的数据--》Model(带着数据)回到了View--》View更新数据
这里也很少再陈述MVC的原理、实践等等,由于这就太长篇大论了。
像咱们以前推理的,分化是一种需求的必然结果,但却没有个一个肯定的结果,好比Code Behind和Code Block的问题等等。
MVC顺着需求把UI相关的工做分化成了三份,这点通过实践证实无可厚非。可是它们的三角关系却被一些人认为带来了一些问题,或者应该说他们有“更好的”解决方案。
在只有Code Behind和Code Block的那个时候维护是很直接的,不是在同一段代码内解决就是在同一个关联的事件上解决。三角关系的问题就是维护问题。在MVC,当你有变化的时候你须要同时维护三个对象和三个交互,这显然让事情复杂化了。
咱们以前说到,随着摩尔定律,软件的需求不断地变化和变得庞大。随着需求变得庞大的时候,需求变化也变得频繁,这是一个出现了无数次之后也将会出现无数的无数次的一个问题,因此它须要一个解决方案,哪怕它不必定能被解决。
为了解决需求变化,从《人月神话》到敏捷到DDD,它不是咱们已经解决了的问题,而是咱们正在解决的问题。放在UI的模式和MVC上来说,就是优化或者替代MVC模式,其中之一就是Model-View-Presenter(MVP)模式。
咱们先看看两个MVP模式的图:
(图一)
(图二)
两幅图是不一样的,可是对MVC的改进的思想倒是同样的:切断的View和Model的联系,让View只和Presenter(原Controller)交互,减小在需求变化中须要维护的对象的数量。
这种方式很符合咱们的期待,由于咱们倾向于:
许多时候并非一种模式很差,而是由于人没办法执行,好比不容易理解,咱们就会选择容易理解的方式。计算机依赖摩尔定律用数量的增加来解决问题,而人是用方式的改变来解决问题的。一样由于客观缘由咱们不善于维护多个对象和多个对象之间的关系,因此咱们改变了,或者说简化了这种方式。
MVP定义了Presenter和View之间的接口,让一些能够根据已有的接口协议去各自分别独立开发,以此去解决界面需求变化频繁的问题。上面两图都有接口,不过接口的实现和使用细节不同,不过思想上是一致的。
在这里要提到的是,事实上,需求变化最频繁的并不必定是最接近用户的界面,但基本能够肯定的是,最接近用户的界面是由于需求变化而须要最频繁更改的。固然,若是View若是是API而不是UI,那就另说了。
还有一些用来“解决”MVC这项缺点的好比有:ASP.NET MVC的ViewBag,Cocoa的delegate。它们都为了简化数据更新的问题而存在,包括MVVM。
先直接看看Model-View-ViewModel(MVVM)的图:
从图上看是比MVP简单了,更不用说MVC了。我的不认为MVVM是从MVP进化而来,我只以为这是在MVP以后出现的一种“更好的”UI模式解决方案,可是用MVP来与之对比比较容易说明问题。
ViewModel大体上就是MVP的Presenter和MVC的Controller了,而View和ViewModel间没有了MVP的界面接口,而是直接交互,用数据“绑定”的形式让数据更新的事件不须要开发人员手动去编写特殊用例,而是自动地双向同步。数据绑定你能够认为是Observer模式或者是Publish/Subscribe模式,原理都是为了用一种统一的集中的方式实现频繁须要被实现的数据更新问题。
比起MVP,MVVM不只简化了业务与界面的依赖关系,还优化了数据频繁更新的解决方案,甚至能够说提供了一种有效的解决模式。
至此,咱们能理解为何许多人认为MVVM是最好的一种模式,没有之一。但事实上,MVVM也是依赖于咱们至今所讲的“特有的情境”。
固然,最优雅的也是第一个能做表明的实践就是Windows Presentation Foundation(WPF)了。
之上,咱们在模式演变的推论基本上都仍是基于桌面软件的,可是过去十年倒是互联网的时代。实际上大部分让你们争议的并非在桌面领域最合适的是那个,而是在Web领域的模式问题,也就是在B/S场景下的问题。
当软件离开单机,去到网络的时候,由于场景变了,因此原有的解决方案也变了,不过需求依然是不变的。咱们依然要解决的问题是用户交互与数据更新的问题,还有维护等等的问题。
当场景变到Web的时候,咱们发现MVVM用来作服务端是极其不适用的,至少如今是不适用的。而MVP你提都不用提。为何呢?由于:
问你们一个问题,当一个网页的数据更新后,你但愿更新用户看到的数据,你会怎么作?
通常状况下,你会:
window.location.reload();
就算你不这么作,用户也会:
F5
就像以前说的,咱们会选择更直接的方式解决问题。直接刷新页面的缘由是由于这样更直接,更容易解决数据更新的问题。
不少时候你不会愿意为了一个数据更新写一个AJAX,更别说这个AJAX要带来Loading、事件顺序处理、网络问题、异常处理等等,这就是开发成本太高。
另外一个网络成本太高就更容易解释了,虽然宽带是基本包月的,但也不带这么用的,况且还有移动用户。更主要的缘由是,在本地软件,更新数据是一个引用问题,而在网络应用上,这是一个传输问题。传输成本远高于引用成本,引用之上顶可能是在本地内存中再进行一次内存拷贝。
这个时候,咱们会更倾向于用MVC模式,由于在Web层面,咱们更倾向于一次性更新数据。
全部问题都不是问题,就算有问题也要解决问题。
为何这个标题下忽然冒出这么一句话?我想说的是,需求依旧是不变的,是推进进步的原动力。
还有我以前说过,当咱们讨论或者争论一个问题的时候,问题的对象已经发生改变了,并且此次是在咱们讨论这个问题以前已经发生改变了。
网络资源成本不断降低,相信已经不须要多说起。摩尔定律和相近的一些原理正在发挥着它应用的做用,网络带宽愈来愈高、相应速度愈来愈快。
若是传输由于相对成本降低而致使数据传输的成本低于开发人员拒绝客户的成本,那么它就会被实现而不是被拒绝。
另外还有一点就是由于技术的进步,技术的资源不断被更大规模地压榨,需求也不断地增加,那么需求始终会增加超过相对不变的开发成本的。好比jQuery的出现解决了不少问题,咱们如今更多地去使用AJAX,哪怕很大一部分依然是为了解决网络资源不足的问题;咱们会用更多的样式代码而用了相对更少的图片;咱们再也不那么依赖Flash一类的矢量图解决方案而直接录制视频。
至少上一节咱们说到的两个致使你们选用MVC的问题都正在被解决,因此咱们有理由相信将来Web不只仅须要MVC,可能会须要MVVM或其余解决方案。至少咱们能理解容易理解为何前端会出现一些MVVM的框架,好比先驱knockout.js和AngularJs。这些框架自己的好坏就不做讨论了,由于咱们讨论的是模式。
在Web上,MVVM的对比对象就不是MVC,而是Code Block。
数据即时更新的需求在扩大,但未必有达到必定要用MVVM这一等级的高大上的模式,实际上若是你要更新一个数据,你仍是会采起:
$('.notice').html('发送成功!');
由于......咱们依然会采起更直接的方式解决问题......
实际上,如今Web MVVM主要并非用在了Web或者Wap上,而是移动App上。按照前面的说法,只多是:
哪怕是Native开发,实际上iOS的开发上也是用相似的数据绑定的方式的。这里也不深究了,毕竟我也不算懂iOS。
要说的是,在Web MVVM或者Web的模式上,也就是Web的富应用上,如今还不过是个初期由膨胀的需求推进的阶段。重要的不是技术会怎么走,而是需求和客观条件会怎么走。
可能Webform会由于高速开发而焕发第二春,它的AJAX的模式也十分知足于简单开发,但彷佛你们须要的不是GUI式的网页。
咱们不必定须要MVVM,但咱们必定须要更强大的方式去解决不断膨胀的Web需求。
咱们能够预见的是:
除去客气话的部分,我仍是想说,在不一样的需求下其实有最适合的解决方案,一般让咱们纠结的不是由于哪一个解决方案更好,而是咱们看到的条件不够多。
编译语言固然比解释语言效率高,但考虑到开发和维护成本,JavaScript等始终会大行其道,好比Node.JS、Python;.NET和微软固然很强大,移植.NET到其余平台也很容易,但微软是家有本身商业模式和要赚钱的公司;固然有些实践和技术更好,但其余开发人员会避开甚至否认本身不擅长的东西,你们都喜欢肯定的东西;有些技术更强大,可是只是基于特殊的客观条件和需求,若是你想作大,要么创造客观条件,要么把它结合需求......
以前有要交流的园友会用园子的短消息,好像我的资料里也没有把邮箱显示出来。有兴趣交流的就直接indreamluo@qq.com吧。