kakashilw 2008-3-10 18:59
MVC模式、类封装还是黑客代码2
[font=Arial][size=3][color=#000000]一个用户登录的例子[/color][/size][/font]
根据以上的代码目录,前文所说的MVC模式的实现可以得到更简单的解释。以最常见的用户登录功能为例,设想/project_name目录下有一个/member目录包含有关于用户的一切功能,其中包含了login.php页面接受用户登录使用的用户名称和密码,index.php页面是登录完成之后的用户主页,而/project_name目录下的error.php是登录失败后的错误显示页面。
[list=1][*]用户通过系统的其他部分请求进入用户主页即/member/index.php页面,此时该页面判断用户情况:已登录用户则直接显示本页内容(可以采用检查session等方法);未登录用户则需要登录(重定向到/member/login.php);出现了未知错误(重定向到/error.php)。同时采取相应的反应。[*]假如用户被引导至登录页面即/member/login.php页面,该页面接受用户的登录信息(用户名称和密码),并判断是否正确登录:正确登录则再次重定向到用户主页/member/index.php;登录错误则重定向到/error.php。[*]假如用户被引导至错误显示页面/error.php页面(无论是从以上哪个页面前来),都会显示错误信息。[/list]流程图示如下:
[img=506,208]http://www-128.ibm.com/developerworks/cn/linux/sdk/php/php_design/fig1.gif[/img]
根据以上的文字描述和图示,再结合MVC模式的实现,可以非常轻松的写出这几个页面的框架代码:
[list=1][*]先看简单的页面/error.php:
[img=404,87]http://www-128.ibm.com/developerworks/cn/linux/sdk/php/php_design/fig2.gif[/img][*]然后是/member/login.php:
[img=554,372]http://www-128.ibm.com/developerworks/cn/linux/sdk/php/php_design/fig3.gif[/img][*]最后是/member/index.php:
[img=192,144]http://www-128.ibm.com/developerworks/cn/linux/sdk/php/php_design/fig4.gif[/img][/list](注意:以上代码只是片断,而且没有考虑项目全局,只起演示作用)
[font=Arial][size=3][color=#000000]关于Controller[/color][/size][/font]
首先可以明确的是,以上的三个页面代码就是前文所说的Controller控制/流程代码。很明显,他们的不包含特定的操作,也没有一行网页代码,有的只是与前面流程图一致的流程控制代码(放眼望去,这些页面的共同特点是充满了引用网页模板并输出、取得对象并执行其某个方法或者重定向)。
再选择其中的一个页面/member/login.php详细的解释。整个页面通过判断是否提交表单分为两个部分:显示登录表单供用户填写和处理登录信息。作为前者直接引用一个处于网页模板目录/Templates下对应该页面的member_login.dwt并在解析后输出;作为后者先取得一个Member对象(该对象出于商业逻辑目录/Include下的Member.inc.php中),然后获得登录判断的结果后进行重定向。在这个控制页面的代码中,member_login.dwt作为View视图/界面出现,类Member作为Model模型/逻辑出现,而页面代码本身就Controller控制/流程。下面就是加入标示的/member/login.php框架代码:
[img=554,372]http://www-128.ibm.com/developerworks/cn/linux/sdk/php/php_design/fig5.gif[/img]
(关于模板类以及在MVC模式中的应用,可以参考我在developerWorks中另一篇文章《 [url=http://www-128.ibm.com/developerworks/cn/linux/sdk/php/template/evaluate/index.html][color=#5c81a7]在PHP世界中选择最合适的模板[/color][/url]》)
[font=Arial][size=3][color=#000000]关于Model[/color][/size][/font]
既然谈到了Model,下面就是另一个重要的话题:类封装在PHP项目中的应用。
请注意用词"类封装"--这和"面向对象"或者其他什么"采用对象设计"的方法有着本质的不同。"类封装"只是讲述了将商业逻辑采用类方法的方式封装成各个不同的类,因而这里的"类"并不是因此采用了面向对象设计出现的"类"--准确的说,这里的"类"其实是对一系列相关功能模块进行合并的结果。
为什么不直接采用面向对象的方式而是采用这种看起来不伦不类的办法去设计系统呢?PHP不是具有面向对象特性吗?不错,PHP具有这样的特性,但是非常不完全(可以参考我在developerWorks的另一篇文章《 [url=http://www-128.ibm.com/developerworks/cn/linux/sdk/php/zend/index.html][color=#5c81a7]从Zend Engine 2.0的设计蓝图(草稿)看PHP的将来[/color][/url]》)。举例来说,PHP是没有接口这一概念和实现方法的,同时也就没有什么多重继承、方法重载之类的典型面向对象特征。如果非要采用面向对象的设计方法,也许在概要设计阶段可以非常轻松,但是详细设计阶段就会比较苦闷,而如果还有幸坚持到编码阶段简直就是苦不堪言了。另一方面,如果不在系统中引入类的概念,而是采用函数来实现模块功能,那么可以想象在一个采用这样"纯粹"的中大型系统中会有多少的函数,由此带来的麻烦非常明显。
还是回到PHP语言本身。虽然PHP提供不了什么实际的面向对象支持,但是还是提供了对类以及其中的属性和方法的定义。那么自然而然可以想到的是采用类的方法封装相关函数模块,既可以借鉴一些对象设计的优点,又可以避免完全采用函数模块的一些缺点。
(一些采用函数模块的系统会采用这样一种方式:将相关的函数编写在相同的文件中,这样在引用时可以引入单独的文件。比如Member.func.php这个文件中包含了所有与用户相关的操作,在处理用户登录时可以先require这个文件,然后调用诸如member_login()这样的函数。但是这样的方式仅仅解决了系统中众多函数的代码组织问题,没有解决名字冲突的问题。下面的举例中就会看到。)