首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

MVC 模式、类封装还是黑客代码 合理设计 PHP 项目-实例涉及大量商业逻辑项目-2

MVC 模式、类封装还是黑客代码 合理设计 PHP 项目-实例涉及大量商业逻辑项目-2

关于 Controller首先可以明确的是,以上的三个页面代码就是前文所说的 Controller 控制 / 流程代码。很明显,他们的不包含特定的操作,也没有一行网页代码,有的只是与前面流程图一致的流程控制代码(放眼望去,这些页面的共同特点是充满了引用网页模板并输出、取得对象并执行其某个方法或者重定向)。
再选择其中的一个页面 /member/login.php 详细的解释。整个页面通过判断是否提交表单分为两个部分:显示登录表单供用户填写和处理登录信息。作为前者直接引用一个处于网页模板目录 /Templates 下对应该页面的 member_login.dwt 并在解析后输出;作为后者先取得一个 Member 对象(该对象出于商业逻辑目录 /Include 下的 Member.inc.php 中),然后获得登录判断的结果后进行重定向。在这个控制页面的代码中,member_login.dwt 作为 View 视图 / 界面出现,类 Member 作为 Model 模型 / 逻辑出现,而页面代码本身就 Controller 控制 / 流程。下面就是加入标示的 /member/login.php 框架代码:
(关于模板类以及在 MVC 模式中的应用,可以参考我在 developerWorks 中另一篇文章《》)
关于 Model既然谈到了 Model,下面就是另一个重要的话题:类封装在 PHP 项目中的应用。
请注意用词 " 类封装 "-- 这和 " 面向对象 " 或者其他什么 " 采用对象设计 " 的方法有着本质的不同。 " 类封装 " 只是讲述了将商业逻辑采用类方法的方式封装成各个不同的类,因而这里的 " 类 " 并不是因此采用了面向对象设计出现的 " 类 "-- 准确的说,这里的 " 类 " 其实是对一系列相关功能模块进行合并的结果。
为什么不直接采用面向对象的方式而是采用这种看起来不伦不类的办法去设计系统呢? PHP 不是具有面向对象特性吗?不错,PHP 具有这样的特性,但是非常不完全(可以参考我在 developerWorks 的另一篇文章《》)。举例来说,PHP 是没有接口这一概念和实现方法的,同时也就没有什么多重继承、方法重载之类的典型面向对象特征。如果非要采用面向对象的设计方法,也许在概要设计阶段可以非常轻松,但是详细设计阶段就会比较苦闷,而如果还有幸坚持到编码阶段简直就是苦不堪言了。另一方面,如果不在系统中引入类的概念,而是采用函数来实现模块功能,那么可以想象在一个采用这样 " 纯粹 " 的中大型系统中会有多少的函数,由此带来的麻烦非常明显。
还是回到 PHP 语言本身。虽然 PHP 提供不了什么实际的面向对象支持,但是还是提供了对类以及其中的属性和方法的定义。那么自然而然可以想到的是采用类的方法封装相关函数模块,既可以借鉴一些对象设计的优点,又可以避免完全采用函数模块的一些缺点。
(一些采用函数模块的系统会采用这样一种方式:将相关的函数编写在相同的文件中,这样在引用时可以引入单独的文件。比如 Member.func.php 这个文件中包含了所有与用户相关的操作,在处理用户登录时可以先 require 这个文件,然后调用诸如 member_login() 这样的函数。但是这样的方式仅仅解决了系统中众多函数的代码组织问题,没有解决名字冲突的问题。下面的举例中就会看到。)
比如上文的用户登录实例中,如果采用函数模块的方法,代码也许是这样:
而采用类封装的方法,可能就是这样:
也许您会觉得代码并没有什么区别(甚至看起来采用函数模块的代码由于不需要取得新的对象而显得更简洁一些),而真正的不同是发生在 include 的文件里面。采用函数模块的方法将相关的函数集合在一个文件中加以组织(有些系统还不能做到这一点,那么就会造成异常混乱的局面),而采用类封装的方法在每一个文件中声明一个和文件名相同的类(比如在 Member.inc.php 声明一个 Member 的类,这一点和 Java 的规定相似);而在使用时,都需要先进行 include(如果采用函数模块又没有进行很好的组织,也许有些人就会很 " 简便 " 的将所有函数 include 进每一个页面 --PHP 可不是 Java 那样编译执行,光是解析这些函数就会花费一段时间),但是关键就在于采用类封装的方法可以清楚的指明调用的位置 -- 某个类(Member)的某个方法(login):从避免名字冲突的角度来说这一点是非常成功的;而对于代码检查和维护而言,方便程度更是不言而喻。设想一个页面需要完成若干功能,因而需要 include 数个文件:采用函数模块的方法不能够轻易的从函数调用中找到函数本身所在的文件(如果函数名称或者 include 文件名称没有什么统一规则,那么这个工作就非常艰巨了),而采用类封装的办法可以根据类名称和类文件名称准确定位类方法代码的位置。(也许您会认为这样一个小小的好处不足挂齿,但是经历一个维护工程之后也许就不会再有什么异议。)
以上是采用类封装方法的原因,决定采用这种方法设计系统只是第一步;完成整个系统的设计还有很多可以借鉴的经验。
  • 部分设计可以借鉴面向对象的思路。虽然 PHP 中没有接口和抽象类的定义,继承机制也非常不完全,但至少具备了基本的类定义和简单的继承关系。类似 " 公司-雇员 "、" 卖家-商品-买家 " 这类显而易见的关系可以很容易在系统中通过类和类关系定义。既然 PHP 可以做到这一点,就按照实际的逻辑关系去定义即可。
  • 经常会在系统中出现的另一个情况是关于个体和列表的关系 -- 这样说也许难以理解,想象一个 BBS 系统中的帖子列表和每个帖子之间,就是这样的关系。根据设计经验,这样的关系大量存在于 PHP 或者其他 Web 系统中。对于这类关系,我个人建议可以采用以下 Item 和 Item_List 的类封装方式:
    • Item 类定义:;
    • Item_List 类定义:。
  • 由于 PHP 对于类的成员变量和方法并没有语法上的访问限制(均为公开),因此会带来对象使用方面的某些混乱。基于此,建议在开发团队的代码规范中加以规定,从代码应用的级别上控制这一情况:
    首先,可以通过对成员变量和方法的注释来说明其属性,由此使用该对象的其他开发人员可以了解自己的使用方法是否触犯了规定的访问限制。(如果采用 phpdoc 等自动文档生成的工具,开发人员甚至可以在不翻阅类源码的情况下通过浏览类文档正确使用它。)
    其次,对于成员变量访问限制的考虑,可以将一些主要的、经常需要被访问或更改的变量(在注释中)声明为公开。这样的作法可以省却大量 get() 和 set() 方法的代码 -- 虽然在其他的面向对象语言中这一点被认为非常丑陋,但是记住 PHP 不是 Java,只要这样的用法合理,就应该大胆使用。
  • 从上面的示例代码中您也许已经注意到了注释的比重 -- 虽然大家都了解注释的重要性,但是仍然有必要提出。这个示例中采用了 Javadoc 的样式,利用现有工具也可以很容易的直接生成文档(当然您和您的开发团队也可以定义自己的合适注释样式和文档生成工具)。对于系统分析员来说,您在设计阶段完成之后交付给您的开发伙伴的代码部分很可能就是这些注释占绝大部分的框架代码;你们之间交流的工具除了那些没完没了的图表之外就是这些程序员最熟悉的代码和注释了。
在 PHP 系统中进行类的设计虽然不像构建面向对象系统那样需要各种合理的模式介入(也没有这样的 " 本钱 " 为之),但还是需要一番思量的。逻辑上的合理性和操作上的可行性都是检验的标准。
(说到类设计,又想到了适合 PHP 开发的 IDE 问题。据我所知比较专业一些有 Zend 出品的 Zend  IDE ;另外还有作为 JBuilder 的 Open  Tools 出现的借助 JBuilder 的 PHP 开发工具;不过最常用的还是 PHPEd 或者 UltraEdit 之类的编辑器。如果现有的编辑器可以非常聪明的支持 PHP 的类设计和代码实现就非常理想了。)
返回列表