晴朗的天空里不会下雨的

无限阳光就在身边

 

用封装类来合理地设计PHP项目--谈PHP项目里面类地封装

 


编码对于合格的PHP程序员来说并不是什么难事(也许只是花费时间长短的问题),因此系统分析和设计这一阶段就显得尤为重要。不过本文并不打算讨论和需求分析、获取商业逻辑相关的话题,而是针对系统设计方面进行探讨。
面临难题
编码对于合格的PHP程序员来说并不是什么难事(也许只是花费时间长短的问题),因此系统分析和设计这一阶段就显得尤为重要。对于一个担任PHP项目的系统分析员来说,面临着两个难题:

PHP语言本身的限制。
这一点在复杂系统的面向对象设计中尤其显著。PHP的面向对象特性在现有版本中虽然得到了改善,但是还不甚健全,根本不足以担任面向对象设计的实现语言;即使眼光长远一些,在即将释出的以Zend Engine 2.0支持的全新PHP中,面向对象特性也不会像现在流行的Java或者C++那样(关于这方面的内容可以参见我在developerWorks中国网站发表的另一篇文章)。但是如果采用完全面向过程(准确说是面向Web页面)的方式,可以想见整个系统的设计会非常复杂,而由此带来的编码复杂和维护困难更加难以应付。
现有资料的严重缺乏。
这是众所周知的现象即针对Web项目的系统设计资料不足;而在这些有限资料中,关于PHP的设计资料又非常匮乏。如果本公司或本人也没有相关的技术积累,系统分析员只能在黑暗中摸索方法(更坏的两种情况,一是照搬其他项目比如Java或者C++的设计,二是认为项目简单而不负责任的草草了事)。



认识面对的系统
既然如此,采用何种方法妥善处理PHP系统的分析和设计?最初的构想应该需要分清项目承担任务的类型:

涉及大量客户本身或者客户所在行业的商业逻辑的项目,包括办公系统、订单系统以及其他商业系统。
简单网站项目,包括一些需要承担高访问量或要求快速响应的项目比如品牌网站或者活动网站以及其他一些网站。
综合性网站项目。通常包含多个相对独立的子系统比如新闻子系统、论坛子系统、产品陈列子系统等等。



PHP的设计初衷在于解决后两种项目的迫切需求,语言本身对于这些项目进行了良好的改造。而众多的PHP开发者对这些项目也具有或多或少的经验,相关书籍中的范例也大都围绕于此。相对说来第一种系统所有的资料不多,各种出版物对其内容也很少提及。因此在本文中将题所述对第一种类型的项目进行详细讲述(有关MVC模式和类封装),同时附带提及第二种项目(有关黑客代码)以及第三种项目的设计方法。当然,并不是被归类的这些项目就只能采用本文描述的方式,系统分析员需要权衡各方面因素加以选择。

方案一:涉及大量商业逻辑项目
如何分离用户界面和后台操作?如何避免将商业逻辑混淆于一般的流程控制中?作为一个严谨的商用项目,就需要考虑很多类似的问题。对于由PHP担当的这类项目,贯彻Model-View-Controller(MVC)模式的设计是一个非常好的方法。

理论描述
在这里我不想多加解释MVC模式本身--简单的从字面上以及应用上说,通过将系统的设计分为Model模型/逻辑、View视图/界面、Controller控制/流程三个逻辑部分达到良好的项目效果,以此便利各部分开发者的工作并降低日后的维护成本。(如果您熟悉JSP开发的Model 2模式,可以发现它也是MVC模式的很好体现。)就现实的项目开发而言,现存的很大问题包括网页设计人员和程序开发人员的工作交错和冲突以及商业逻辑嵌入页面造成不可重用也很难维护等等。引入MVC模式一方面可以为系统的总体设计指出明确的方向,对于开发团队的分工也是良好的指导。

既然依照MVC模式要求对系统的总体结构在逻辑上分成三部分,那么团队的开发者中也存在着针对各个部分的开发者。

开发者角色 相关系统逻辑 职责
网页设计人员 View视图/界面 设计所有用户界面的网页模板。
控制流程开发人员 Controller控制/流程 编写系统流程中的所有PHP页面。
商业逻辑开发人员 Model模型/逻辑 开发系统设计中规定的各个类(其中的方法)。



由以上的表格可以看出,传统的网页设计和程序开发的人员分工被打破而取而代之的是根据系统逻辑划定的职责。对于网页设计人员,职责并没有改变,准确说由于这样的划分避免了以往与程序设计人员的纠纷,他们完成的只是网页模板,因此只需关注于纯粹的网页代码(主要是HTML,也许会有其他客户端的代码比如WML之类)而根本不需要被服务器端的<? … ?>干扰。程序设计人员则被分为两部分:其中比较容易把握的是商业逻辑开发人员,他们的任务是根据系统分析员给定的模块(准确说是类方法)完成之,在他们手中的PHP更像一般的程序设计语言(比如Java)而与Web没有什么关系;一时较难接受的是控制流程开发人员,他们的任务是在实现系统设计时制定的系统流程的同时,根据客户端的输入调用商业逻辑(相应的类方法)以及输出更新的界面(对设计网页模板进行处理),在他们手中PHP可以充分发挥Web编程语言的优势。

代码组织相关的话题
这样的观念有些抽象,没有实例的演示很难接受。在举例之前先介绍一下我对这类工程的推荐代码结构:

一级目录 二级目录 三级目录 备注
/project_name 项目源代码根目录
/Templates 网页模板目录(View)
/admin 管理控制台目录/admin下的网页模板
/Include 商业逻辑目录(Model)
/Temp 临时代码目录(可选),可供开发者进行一些试验代码的测试
/images 图片目录,网页模板采用
/css 样式单目录,网页模板采用
/scripts 客户端代码目录,网页模板采用
/admin 管理控制台目录(可选),包含所有后台管理的功能代码
/other_dir 对应与源代码根目录下的/other_dir,包含管理该类的功能代码
/other_dir 其他与相应功能相关的目录,比如与用户相关的/member目录或者与从产品相关的/product目录等等
/config.inc.php 全局配置变量,定义系统中的全局变量
/security.inc.php 安全策略控制(可选)
/error.php 错误控制返回页面(可选),也可以采用静态页面如/error.html或者其他页面名称



看完之后您是不是被唤起了一点使用Java进行Web开发的记忆?比如WEB-INF目录下的classes目录和lib目录以及web.xml都是开发中的规则--虽然支持PHP的Web服务器不可能像Java应用服务器那样自动加载这些目录下的文件,但是规定一个合适的代码组织模式还是非常有利于开发的便利和后续的维护的。(我开始考虑PHP项目的代码结构就是由Tomcat的开发手册中获得了启发。)

一个用户登录的例子
根据以上的代码目录,前文所说的MVC模式的实现可以得到更简单的解释。以最常见的用户登录功能为例,设想/project_name目录下有一个/member目录包含有关于用户的一切功能,其中包含了login.php页面接受用户登录使用的用户名称和密码,index.php页面是登录完成之后的用户主页,而/project_name目录下的error.php是登录失败后的错误显示页面。

用户通过系统的其他部分请求进入用户主页即/member/index.php页面,此时该页面判断用户情况:已登录用户则直接显示本页内容(可以采用检查session等方法);未登录用户则需要登录(重定向到/member/login.php);出现了未知错误(重定向到/error.php)。同时采取相应的反应。
假如用户被引导至登录页面即/member/login.php页面,该页面接受用户的登录信息(用户名称和密码),并判断是否正确登录:正确登录则再次重定向到用户主页/member/index.php;登录错误则重定向到/error.php。
假如用户被引导至错误显示页面/error.ph
而采用类封装的方法,可能就是这样:





也许您会觉得代码并没有什么区别(甚至看起来采用函数模块的代码由于不需要取得新的对象而显得更简洁一些),而真正的不同是发生在include的文件里面。采用函数模块的方法将相关的函数集合在一个文件中加以组织(有些系统还不能做到这一点,那么就会造成异常混乱的局面),而采用类封装的方法在每一个文件中声明一个和文件名相同的类(比如在Member.inc.php声明一个Member的类,这一点和Java的规定相似);而在使用时,都需要先进行include(如果采用函数模块又没有进行很好的组织,也许有些人就会很"简便"的将所有函数include进每一个页面--PHP可不是Java那样编译执行,光是解析这些函数就会花费一段时间),但是关键就在于采用类封装的方法可以清楚的指明调用的位置--某个类(Member)的某个方法(login):从避免名字冲突的角度来说这一点是非常成功的;而对于代码检查和维护而言,方便程度更是不言而喻。设想一个页面需要完成若干功能,因而需要include数个文件:采用函数模块的方法不能够轻易的从函数调用中找到函数本身所在的文件(如果函数名称或者include文件名称没有什么统一规则,那么这个工作就非常艰巨了),而采用类封装的办法可以根据类名称和类文件名称准确定位类方法代码的位置。(也许您会认为这样一个小小的好处不足挂齿,但是经历一个维护工程之后也许就不会再有什么异议。)

以上是采用类封装方法的原因,决定采用这种方法设计系统只是第一步;完成整个系统的设计还有很多可以借鉴的经验。

部分设计可以借鉴面向对象的思路。虽然PHP中没有接口和抽象类的定义,继承机制也非常不完全,但至少具备了基本的类定义和简单的继承关系。类似"公司-雇员"、"卖家-商品-买家"这类显而易见的关系可以很容易在系统中通过类和类关系定义。既然PHP可以做到这一点,就按照实际的逻辑关系去定义即可。
经常会在系统中出现的另一个情况是关于个体和列表的关系--这样说也许难以理解,想象一个BBS系统中的帖子列表和每个帖子之间,就是这样的关系。根据设计经验,这样的关系大量存在于PHP或者其他Web系统中。对于这类关系,我个人建议可以采用以下Item和Item_List的类封装方式:
Item类定义:Item.inc.php的代码
Item_List类定义:Item_List.inc.php的代码
由于PHP对于类的成员变量和方法并没有语法上的访问限制(均为公开),因此会带来对象使用方面的某些混乱。基于此,建议在开发团队的代码规范中加以规定,从代码应用的级别上控制这一情况:
首先,可以通过对成员变量和方法的注释来说明其属性,由此使用该对象的其他开发人员可以了解自己的使用方法是否触犯了规定的访问限制。(如果采用phpdoc等自动文档生成的工具,开发人员甚至可以在不翻阅类源码的情况下通过浏览类文档正确使用它。)
其次,对于成员变量访问限制的考虑,可以将一些主要的、经常需要被访问或更改的变量(在注释中)声明为公开。这样的作法可以省却大量get()和set()方法的代码--虽然在其他的面向对象语言中这一点被认为非常丑陋,但是记住PHP不是Java,只要这样的用法合理,就应该大胆使用。
从上面的示例代码中您也许已经注意到了注释的比重--虽然大家都了解注释的重要性,但是仍然有必要提出。这个示例中采用了Javadoc的样式,利用现有工具也可以很容易的直接生成文档(当然您和您的开发团队也可以定义自己的合适注释样式和文档生成工具)。对于系统分析员来说,您在设计阶段完成之后交付给您的开发伙伴的代码部分很可能就是这些注释占绝大部分的框架代码;你们之间交流的工具除了那些没完没了的图表之外就是这些程序员最熟悉的代码和注释了。



在PHP系统中进行类的设计虽然不像构建面向对象系统那样需要各种合理的模式介入(也没有这样的"本钱"为之),但还是需要一番思量的。逻辑上的合理性和操作上的可行性都是检验的标
【摘自酷网学院,有待继续】

posted on 2008-05-30 17:15  xinyult  阅读(227)  评论(0编辑  收藏  举报

导航