转载自:http://blog.sina.com.cn/s/blog_72e747600100o7rc.html
pubDate:2010-04-06 15:41:22
“什么是Framework?Library又是什么?它们有什么区别吗?你对MVC了解多少?”在大约两年前,也就是2008年初,坐在我对面的一个大鼻子老外这样问我。虽然我得到了一次复试的机会,但是在复试时他仍旧问了这些我认为毫无价值的问题,结果我被“放弃”了,考官的表情比我还无奈。我当时只是觉得一定是因为言语不通,并没有想太多“为什么”,甚至没有再想想他反复问过的这三个问题。没办法,我这个人开窍总是比别人慢那么半拍。虽然Framework、Library和MVC这三个词不能帮你升职、加薪,或许你也只是面试时才拿来炫耀一下;但事实上,对于它们的了解直接决定了你作为一名程序员的职业素养,学过多少种编程语言其实都不重要,重要的是知道如何用已经懂得的语言思考问题并解决问题。
Library
现在,我还是来阐述一下我理解中的Library是什么。
和“面向对象编程”相对的是“面向过程编程”,但请相信我,这两个词基本上是同一时期出现的,在此之前比较流行的说法是“模块化编程”,当时最具代表性的高级语言便是C语言,如果论及语法的严谨性则不得不提到Fortran。举个简单的C语言例子,来说明什么是模块化编程,如下:
#include “A.h”
#include “B.h”
#include “C.h”
#include “D.h”
#include “E.h”
main() {
DO_A();
DO_B();
if (C()) { DO_D(); } else { DO_E(); }
return 0;
}
如果一个程序需要做A、B、C、D或E五件事,我们并不需要把这五件事的逻辑全部写在主程序逻辑(即main函数)中;因为如果这样做,那么当其中一个逻辑(比如A)发生变化时,我们将不得不修改整个主程序逻辑;且,随着程序复杂程度的增加,实现各个功能的代码块之间的耦合度也会逐渐增加,到最后牵一发而动全身,以致整个程序无法在进行维护。更何况,如果其他程序也需要用到本程序中已经实现过的功能,则它不得不在自己的程序中将这些逻辑重新实现一遍;不仅浪费了时间和精力,而且还带来一大堆问题。要改变这种情况,我们便将可以独立为A、B、C、D、E五种不通操作的逻辑从主程序逻辑中分离出来,并单独封装成功能模块,而每个模块有一个基本上固定不变的接口,就如上例程序中所示;我们甚至可以封装一个模块F,根据C模块的运行结果来自动判断调用D模块还是E模块;总之,构成程序的将不再是一行行独立的代码,而是不同模块相互堆叠和包含,这就是所谓“模块化编程”,而对于主程序逻辑来说,已经封装好的模块A、B、C、D、E则被称为"Library”,这个Library可以供多个程序调用,并且其接口相对来说是固定不变的。
何为Library已经很清楚了吧,虽然绕了一个大圈子,但是应该还是很好理解的;简单的说,实现了特定功能,提供统一接口,并可供主程序逻辑调用的便是Library。
Framework
那么,Framework是什么呢?我觉得它和Library正好相反,Libraray是用来被你的程序调用的,然而Framework则是用来调用你写的程序;请看下图:
┌─┐
│E│
│V│ ┌─┐
│E│┈┈┈┈----┈>│R│
│N│ │O│(filter) ┌─┐
│T│ │U│ ↓ │A│
└─┘ │T│┈┈--┈ >│C│
│E│ │T│
┌─┐ │R│ │I│
│R│ └─┘ │O│
│E│<┈┈┈┈┈┈┈┈┈┈┈│N│
│S│ ↑ └─┘
│U│ (hooker)
│L│
│T│
└─┘
这是目前大部分Framework的运行流程(当然,肯定有个别情况):当一个事件(EVENT)被触发,它将携带它的全部信息并被抛给一个路由规则(ROUTER),路由规则根据事件所携带的信息来选择并执行相应的动作(ACTION),最后给出一个结果(RESULT);无论你的程序实现何样特殊的功能,无论你有多少这样特殊的程序,只要它们使用了同一个Framework,那么他们的运行流程都大致相似;我们可以编写很多实现不同功能的ACTION,用不同的方式来展现ACTION返回的结果(RESULT),甚至我们可以定义我们自己的路由规则,但是我们始终无法改变整个程序的处理顺序,即EVENT->ROUTER->ACTION->RESULT。我说过“Framework是用来调用你写的程序”,你现在明白了吗?
简单举个例子;假设目前有一个WEB项目和一个PHP框架,并且,该框架的运行流程与上图相同。那么很自然,当用户的浏览器向服务器发送一个请求时,上图中事件(EVENT)即被触发,而事件所携带的信息便包含在浏览器发给服务器的请求中(该请求用MIME格式封装,可以简单了解一下HTTP协议)。当服务器端的PHP程序接收到这个事件后,会将其交给一个路由规则(ROUTER),通常这个路由规则是框架内部的一段PHP代码,它负责分析事件信息(比如分析URL),来决定调用哪个ACTION来完成这次请求。在这里ACTION应该是实现了某个功能的PHP代码,它们并不是框架的一部分,但是它们的存在却是为了被框架调用,以实现它们自己的功能,随着项目的进展这些ACTION的数目会慢慢增加。最后,ACTION执行后的结果将被编制为用户浏览器可以识别的格式,并由服务器返回给用户的浏览器。利用这个框架开发WEB项目的程序员,只需要实现不同功能的ACTION,并按照用户浏览器可识别方式编制其结果就可以了,除此之外的一切细节都理应有框架来处理。
那么,我们这么做有什么好处呢?貌似学习起来还需要些时间。其实显而易见,首先框架可以最大限度的封装一些技术细节,比如处理WEB项目中的GET或POST参数(使用PHP来开发WEB项目是很方便的,但如果是用Python你就知道框架的重要性了)。其次,统一代码风格,程序员的喜好个有不同,有人喜欢面向对象,有人喜欢面向过程;同一个项目中出现风格迥异的代码,不仅降低代码的可重用性,增加维护成本,更糟的是破坏你的好心情。再次,高可移植性,通常框架都会以一种比较好的方式来组织你代码,基本上大部分框架都实现了MVC(这个后面会讲);想想看,在一个WEB项目中,如果某一天公司决定将数据库服务器换成ORACLE,这时你会怎么办?如果公司决定要在现有应用的基础上开发一个WAP版,你又该怎么办?如果之前没有好好组织过你的代码的话,那么你现在是不是很抓狂?
MVC
好的,还有一点点,现在可以来说说什么是MVC了。明确一点,MVC就是一种设计模式(完全符合设计模式的六个原则:“开-闭”,里氏代换,合成复用,依赖倒转,接口隔离,抽象,迪米特法则;本文不再具体描述什么是设计模式)。
其实,基本上所有的需要与用户交互的项目都会面临这样两个问题:一,数据存取操作与数据源的无关性;二,客户端界面样式与功能逻辑的无关性。为了能够解释清楚,我们假设有一个PHP的WEB项目,如下图:
┌─────┐ ┌─────┐
│A.PHP │ │B.PHP │
└─────┘ └─────┘
│ │
│ │
│ ←read read→│
└───────────────┘
│
│↓write
┌───────┐
│DB:C │
│TABLE:D│
└───────┘
可以看到两个PHP页面都需要读写同一个库里面的同一张表;但如果这时表D的结构发生变化(比如增加了一个字段,或者由原来的一张表拆分成了D_00至D_99的一百张表),我们不得不修改A.PHP和B.PHP中对表D的读写操作的逻辑,以便让他们都能正常工作;好在访问D表的只有这两个页面,否则这一点小小的改动就意味着加班、埋怨和意料之外的BUG。另外,假设A.PHP经过多次的修改和“维护”,其中代码恐怕只有“上帝”能看懂了。这时公司决定做一个A.PHP的WAP版,你首先想到的是给A.PHP传个参数,以便让它自己判断是要显示HTML页面或WML页面,但是此时A.PHP混乱的代码让你感觉无从下手。无奈之余,你还是要把代码拷贝到A_WAP.PHP中,把代码从头梳理一下,保留主要逻辑,去掉其中与显示HTML有关的代码,添加显示WML的代码。这样真的就完事大吉了吗?由于一些原因,你又要修改A.PHP了,下班时你念叨着“可算做完了,下次不要再让我做这个!”,可是产品人员会提醒你,“呃…,你看A_WAP.PHP是不是也要改一下?这对我们很重要。”你很清楚这意味着什么。
MVC最初被提出来,就是为了解决以上两个问题,最初还只是用于桌面应用的项目。MVC模式虽然没有明确的定义,但普遍认同的解释是这样的:将项目分为Model,Controller,View三个部分;Model层负责处理主要业务逻辑,通常包括数据源的读取操作;Controller负责接收用户的请求,并根据该请求调用对应的Model和View;而View则只专注于用户界面的显示样式。具体如下图:
┌─┐
│U│
│s│ request ┌─┐
│e│┈┈┈┈┈>│ │
│r│ │ │
└─┘ │C│
setData │ │ show ┌─┐
┌─┐<┈┈┈┈┈│ │┈┈┈┈> │ │
│ │ └─┘ │ │
│M│ │V│
│ │<┈┈┈┈┈┈┈┈┈┈┈│ │
└─┘ getData └─┘
从上图中可以看出M、V、C三个部分是完全隔离的,这三个部分分别通过"setData”等接口为其他部分提供服务,只要接口不发生改变,任何一个部分内部发生的变化都不会影响其他部分,也就是所谓的“低耦合性”(想想看,只需要修改一个Model类,就可以让你所有访问那个已经变化了数据结构的程序都能够正常工作,该是一件多么惬意的事)。这些特点使得MVC更像是一个三层结构,所以有些人喜欢称它们为“C层~,M层~,View层~”,这种叫法绝对没问题;而且上图中表现的是一个理想的MVC结构,其实在很多实现了MVC的框架中,“View层”是必须要通过“C层”来访问“M层”的。更重要的是,对于已经实现过的业务逻辑(即Model),它可以被任何一个控制器(即Controller)调用,不需要重新实现一次;而且,它们应该可以被继承,甚至是被包含在其他Model中,这即所谓“高可重用性”。最后,关于MVC的实现方式就不再赘述了,实现了MVC的框架有很多,由于MVC没有一个明确的定义,所以实现方式千差万别,只有在工作中长期实践才能找到真正适合自己的方法。
好了,以上都是我的一家之言,难免有些主观偏见。从06年改行做程序员以来,已经将近四年时间,其实我的很多看法也在根据实践经验一点点的修正;谈到收获我只能说“程序能跑就喔凯”绝对不是一个程序员该有的素养。有人说中国程序员都是吃青春饭,我也不知道这一行究竟还能干多久;但是我觉得,如果都做不好一个程序员,又如何能去管理程序员?至少要对自己负责。