Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf ·...

26
Symfony、CakePHP 和 Zend Framework 简介 本章主要内容: 框架简介 主流 PHP 框架简介 设计模式 我们都知道,所有 Web 应用程序都有某些共同之处:用户都能够进行注册、登录和交 互。交互通常通过已经验证的安全形式实现,结果存储在各种数据库中。Web 应用程序先 搜索数据库,处理数据,再根据用户地址提供数据。如果将这些模式提取为某种类型的抽 象,然后将这些抽象传送到更多应用程序中,那么可以加快开发过程。 我们显然可以做到这一点,而且能够以多种不同方式使用几乎所有编程语言做到这一 点。正因为如此,许多解决方案都能够使 Web 开发更方便快捷。本书提供 3 种解决方案: SymfonyCakePHP Zend Framework。这 3 种解决方案不仅能够大大加快开发过程,而 且能够提供 Web 2.0 应用程序必不可少的许多高级功能。 1.1 Web 应用程序框架的定义及其用法 Web 应用程序框架是归入某种特定的体系结构中的一批源代码,可用于快速开发 Web 应用程序。可以认为框架是一种半成品的应用程序,人们可以依据需求将它们扩展为成品。 有人认为这意味着任务已经完成了一半,但在某种程度上说这只是一种美好的愿望,因为 1

Transcript of Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf ·...

Page 1: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

SymfonyCakePHP 和 Zend

Framework 简介

本章主要内容

框架简介

主流 PHP 框架简介

设计模式

我们都知道所有Web 应用程序都有某些共同之处用户都能够进行注册登录和交

互交互通常通过已经验证的安全形式实现结果存储在各种数据库中Web 应用程序先

搜索数据库处理数据再根据用户地址提供数据如果将这些模式提取为某种类型的抽

象然后将这些抽象传送到更多应用程序中那么可以加快开发过程

我们显然可以做到这一点而且能够以多种不同方式使用几乎所有编程语言做到这一

点正因为如此许多解决方案都能够使 Web开发更方便快捷本书提供 3种解决方案

SymfonyCakePHP 和 Zend Framework这 3种解决方案不仅能够大大加快开发过程而

且能够提供Web 20应用程序必不可少的许多高级功能

11 Web 应用程序框架的定义及其用法

Web应用程序框架是归入某种特定的体系结构中的一批源代码可用于快速开发 Web

应用程序可以认为框架是一种半成品的应用程序人们可以依据需求将它们扩展为成品

有人认为这意味着任务已经完成了一半但在某种程度上说这只是一种美好的愿望因为

1 第 章

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

2

这项工作是以特定方式完成的缺乏监督机制

所有框架要么都包含一种编码方法以及一些命名和结构约定要么如果要摆脱这些限

制就需要用户自己重新配置这样做将会降低灵活性或者让学习这些方法的过程变得

更加困难如果确实希望避免这些问题而使用类似于库的方法那么不得不牺牲开发速度

因此所有框架都是一种折中

因此最好先了解多种框架并比较它们之间的差异不管怎样也许其中某种框架能够

提供更合适的约定也许可以使用某种初始配置实现快速灵活的开发也许你只是想要一

个功能强大的组件库由你自己将它们链接起来选择权在于你如果能够避开这些框架

的弱点那么将充分享受它们的最大好处真正实现快速开发

框架的优点还包括代码简洁能够尽量降低编程错误所带来的风险框架遵循ldquo不重

复自己rdquo(DRY)的原则也就是说在一个地方只对所有逻辑进行一次编码该规则禁止复

制代码特别是复制再粘贴这有利于代码维护能够防止出现严重错误一般来说框

架提高了代码的可重用性提供了良好的编程实践对于没有足够专业知识或纪律来关注

代码质量的编程人员而言这非常重要

另一个重要功能是干净有组织的链接外观(可以使用 URL重写实现)大多数框架支持

这种功能例如不是输入animalsphpspecies=catsampbreed=mainecoon而是输入animalscats

mainecoon后者不仅看起来更简洁而且有利于搜索引擎优化(Search Engine Optimization

SEO)

111 框架与库

库与框架的主要区别在于

从代码调用库

框架调用代码

换句话说应用程序的框架是可以填充功能的骨架或者是一个可以在上面构建模块

的平台而库则是在你自己构建的平台顶部提供附属模块有些人认为框架比库更好更完

整因此经常滥用ldquo框架rdquo这个术语因此也有人将某些库称为框架即使它们没有调用

开发人员的代码将一块代码称为库没有错因为它只是不同的实体而已也有一些不好

的框架损害了好框架的名声mdashmdash基本上是发布半成品的应用程序然后将它称为框架这

两种软件组大相径庭不要将其混淆

框架利用的应用程序体系结构称为控制倒置(inversion of control)因为相对于一般过

程化编程来说它的数据流是颠倒的这也称为好莱坞原则ldquo不要给我们打电话我们会

打给你rdquo它与调用开发人员代码的第三方代码相符这么做的主要原因是让高级组件减少

对其子系统的依赖高级组件将控制传递给低级组件低级组件自己决定如何运行与何时

响应例如命令行程序与包含用户界面窗口的程序就不相同命令行程序停下来要求用

户输入而在包含用户界面窗口的程序中用户只须单击按钮再由窗口管理器调用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

3

有些框架(例如 Zend Framework 或 CodeIgniter)遵循松散耦合的体系结构也就是说

它们的组件彼此很少相互依赖甚至可以单独使用这更像库的风格虽然松散耦合的框

架提供的开发速度不及紧密耦合的体系结构和模型-视图-控制器(Model-View-Controller

MVC)模式所提供的速度但这种方法更加灵活代码的可控性也更强

112 使用框架的时机

框架并不能解决所有编程问题抛开今天开发的狂热状态你应该还记得几年前是如

何创建框架的当时大多数框架几乎可以说是无法有效利用的垃圾人们创建这些框架只

是为了加速开发过程根本没有关注文档优雅性易用性或代码的可读性另一群人使

用了这些代码并给它们填充了一些与原始代码几乎不相符的其他功能为了便于使用

必须清除代码但这不是完全重写也不是将代码包装到其他包装类里因为这样只会进

一步增加代码的复杂性

当然今天框架的杂乱无序不像以前那样明显因为代码的质量已经大大提高但这

既是大多数框架存在性能问题的原因也是不易掌握它们的原因同时是新框架掩盖旧框

架弱点的原因最后这也是主流框架提供全新 20 版本的原因这个版本解决了前面提

到的所有问题

1 优点

在下列情况下适合使用 Web 应用程序框架

几乎包含动态内容的所有标准项目例如社交网联机商店新闻门户网站等

易于扩展的应用程序这些应用程序可以从头发展为全球普及服务而不需要对代

码做大的变更

生成连续的应用程序其中代码(例如控制器和视图)的模块性和可重用性都非常

有用

包含最后期限员工流动和不固定客户的现实开发

如果你是专业Web 开发人员或者希望成为专业Web开发人员就必须学会如何使

用框架

这些情况适用于大多数商业 Web应用程序这些应用程序连接到数据库并允许用户

创建和修改数据库中的内容因此在 Web 开发领域中使用 Web 应用程序框架编程已

成为标准实践

2 缺点

在下列情况下应该考虑不使用框架进行开发

不包含用户创建内容的纯粹提供信息的网页例如包含奇妙图形的艺术作品

包含有限数据库连接的小项目它们不能从框架的代码生成中受益

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

4

需要提供高性能的大项目例如 Google 套件(可以使用经过编译的编程语言而不

是 PHP)

需要提供最好的性能却只包含有限硬件资源的项目(因为现在编程成本总是高于硬

件成本)

专业或实验性应用程序它们可能涉及完全未知的方向或使用某些自定义解决方

案例如用于科学试验的界面(包含面向对象数据库)

当真正需要(并能提供)完全控制代码和应用程序的演变时

当你想创建一个Web应用程序而你的合伙人不想或者甚至不会使用框架时

这些条件通常体现在以下 3种类型的项目中小型静态网站非常专业的网站和失败

的网站创建框架是为了使用标准体系结构开发常见的 Web应用程序当然可以使用插

件或模块来扩展它们但是完全更改其结构则要付出艰辛努力而且需要不断检查它们的

功能与项目的设计需求是否一致

113 PHP 与其他编程语言

多年以来PHP 一直是一种非常普及的编程语言但人们普遍认为它不够专业墨守

陈规的 PHP 开发人员都是生产廉价低质量代码而又未受过良好教育的自由人专家们推

荐使用 ZopeASP 或者各种 Java技术2005年Ruby得到了迅猛发展每个人对这种编

程语言的优雅性都惊叹不已用 Ruby 语言编写的软件 Ruby on Rails 被认为是最完美的

Web应用程序框架不久Ruby on Rails的克隆版开始涌现同时也出现了 Python的 Django

和 Turbogears以及各种 PHP 框架

PHP5 于 2004年发布这是一种面向对象的简洁编程语言如果有人写的老式 HTML

仍然混合了一些 PHP 脚本片段那么这只是他自己的选择并不是编程语言的问题一段

时间之后人们才逐渐将 PHP 作为一种规范的专业工具使用PHP 和现代MVC 范例以及

其他框架功能一起工作成为了开发 Web应用程序的首选语言

多年之后Ruby on Rails 出现了许多限制其中一个重要限制就是低可用性和 Ruby

托管的高成本而 PHP 有许多廉价的托管有许多团体希望开发早期的 PHP 框架这导

致了一场 IT 革命这场革命颠覆了 Ruby on Rails 作为最受欢迎框架的地位而用 PHP 框

架取而代之

图 1-1显示的是各种框架在 Google 搜索引擎的 Computers amp Electronics类别中搜索量

随时间的变化情况该图是使用 Google Insights for Search 创建的它是有名的 Google

Trends工具的高级形式可以在 wwwgooglecominsightssearch网站上自己搜索这些条目

查看 2011年的搜索结果

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

5

图 1-1 不同编程语言中框架的搜索量

12 开源 PHP Web 框架

另一个需要回答的问题是为什么选择这 3种特定框架它们是否真的更好还是我们

有偏见又或者是推广它们有某种商业利益先回答最后一个问题我们是完全独立的开

源拥护者我们只比较免费(就像免费演讲一样)软件因此我们背后没有利益集团也没

有人告诉我们应该选择哪种框架下面几节将回答它们是否比其他框架更好这个问题

121 公众关注的框架对比

我们之所以选择 SymfonyCakePHP 和 Zend Framework是因为它们在 Web 开发团

队中非常普及并且我们有使用 PHP 的经验我们认为开源编程工具在普及度和质量之

间至少有某种相关性只有它们有用人们才会使用就这一点而言它们不同于专有软

件或流行音乐专有软件或音乐可能通过迅猛的市场营销来实现普及性从而轻易地取代

人们对质量的关注

之前曾经也有过闭源 PHP框架但由于免费框架取得了广泛成功现在闭

源框架已经成为了过去

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

6

事实证明可以更为客观地反映公众对Web框架的兴趣图 1-2显示的是Google Insights

for Search中不同 PHP 框架的搜索量从中我们可以看出主要有 4个竞争者其他 Web框

架加起来还不及这 4个框架中的一个Lithium和 Prado 框架直接被省略了因为它们的名

称不是唯一的容易产生混淆我们在特定类别中搜索过这两个名称发现它们并不是重

要的搜索项

图 1-2 不同 PHP 框架的搜索量对比

用户搜索与框架相关的信息时结果通常是各种博客和论坛上的讨论情况以及关于

如何学习这种技术的有关条目和关于如何使用这些框架开发应用程序的相关条目由此我

们可以看出公众关注的是如何真正长期使用某种 Web 框架

对于我们而言真正有争议的是 CodeIgniter框架关于是否将它作为主要框架之一

我们争论了很长一段时间也许现在它的搜索频率与 Symfony 和 CakePHP 框架相当但

更重要的是图 1-2 中下面的区域它反映有多少人找到了答案并可能在他们的项目中使用

这种方法

当然图 1-2 中的曲线图只反映搜索量就这种增长曲线来看很难区分长期趋势和

临时现象我们知道 CodeIgniter框架非常好因此它绝对不只是一种潮流也许一两年之

后它将在主要Web工具中占有一席之地

最后我们决定讨论 3种框架但我们没有完全放弃对 CodeIgniter框架的讨论在附录

B 中我们将对它的功能进行描述其中也将介绍 Lithium 和 Agavi 框架在该附录中还将

使用每种框架开发一个简单的应用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

7

122 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息它们的网站上也只有营销性

描述在它们的网站上只是列举了这 3种框架的某些相似功能

Symfony 框架是一种全栈框架是一种用 PHP 语言编写的关联类库它为开发人

员提供了一种体系结构以及一些组件和工具以方便他们更快地构建复杂的 Web

应用程序选择 symfony 框架能让你更早发布应用程序托管升级以及维护它们

都没有问题Symfony框架并不是完全重新创造的而是基于经验的它使用大多

数Web开发的最佳实践同时集成某些第三方库

CakePHP 框架是一种快速开发框架它为开发维护和部署应用程序提供了一种可

扩展的体系结构通过在配置范例约定内使用常见的设计模式(例如MVC和ORM)

CakePHP 框架可以降低开发成本让开发人员写更少的代码

Zend Framework 是 PHP 的扩展它基于简单性面向对象的最佳实践公司友好

的许可和经过严格测试的敏捷代码库Zend Framework 致力于构建更安全更可靠

更现代的Web 20 应用程序和Web服务

你是否能够说出这三者之间的不同网站并没有提供这些框架功能的信息在各种博

客和论坛上虽然可以找到一些信息但缺少经过验证的数据而且一般讨论倾向于交流个

人观点

这就是我们写这本书的原因实际上框架之间的不同并不明显需要用一段时间对

它们进行验证也需要用一些实际示例来进行说明然后才能推广为商业解决方案下面

从最基础的内容开始

1 Symfony

开始时间2005年

许可证MIT

PHP 版本

Symfony 14 PHP 524+

Symfony 20 PHP 53+

其徽标如图 1-3 所示网址是 wwwsymfony-

projectorg

Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建开发人员是 Fabien

Potencier它最初用于开发该公司自己的应用程序2005年发布为一个开源项目其名称

是ldquosymfonyrdquo但有时为了醒目起见而将首字母大写(本书就是这么做的)

Symfony框架基于古老的Mojavi MVC 框架必然也受到 Ruby on Rails 的一些影响

它集成了 Propel 对象-关系映射器并且将 YAML Ainrsquot 标记语言(YAML Ainrsquot Markup

LanguageYAML)系列标准用于配置和数据建模默认的对象-关系映射(ORM)解决方案后

来演变成了 Doctrine

图 1-3 Symfony徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 2: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

2

这项工作是以特定方式完成的缺乏监督机制

所有框架要么都包含一种编码方法以及一些命名和结构约定要么如果要摆脱这些限

制就需要用户自己重新配置这样做将会降低灵活性或者让学习这些方法的过程变得

更加困难如果确实希望避免这些问题而使用类似于库的方法那么不得不牺牲开发速度

因此所有框架都是一种折中

因此最好先了解多种框架并比较它们之间的差异不管怎样也许其中某种框架能够

提供更合适的约定也许可以使用某种初始配置实现快速灵活的开发也许你只是想要一

个功能强大的组件库由你自己将它们链接起来选择权在于你如果能够避开这些框架

的弱点那么将充分享受它们的最大好处真正实现快速开发

框架的优点还包括代码简洁能够尽量降低编程错误所带来的风险框架遵循ldquo不重

复自己rdquo(DRY)的原则也就是说在一个地方只对所有逻辑进行一次编码该规则禁止复

制代码特别是复制再粘贴这有利于代码维护能够防止出现严重错误一般来说框

架提高了代码的可重用性提供了良好的编程实践对于没有足够专业知识或纪律来关注

代码质量的编程人员而言这非常重要

另一个重要功能是干净有组织的链接外观(可以使用 URL重写实现)大多数框架支持

这种功能例如不是输入animalsphpspecies=catsampbreed=mainecoon而是输入animalscats

mainecoon后者不仅看起来更简洁而且有利于搜索引擎优化(Search Engine Optimization

SEO)

111 框架与库

库与框架的主要区别在于

从代码调用库

框架调用代码

换句话说应用程序的框架是可以填充功能的骨架或者是一个可以在上面构建模块

的平台而库则是在你自己构建的平台顶部提供附属模块有些人认为框架比库更好更完

整因此经常滥用ldquo框架rdquo这个术语因此也有人将某些库称为框架即使它们没有调用

开发人员的代码将一块代码称为库没有错因为它只是不同的实体而已也有一些不好

的框架损害了好框架的名声mdashmdash基本上是发布半成品的应用程序然后将它称为框架这

两种软件组大相径庭不要将其混淆

框架利用的应用程序体系结构称为控制倒置(inversion of control)因为相对于一般过

程化编程来说它的数据流是颠倒的这也称为好莱坞原则ldquo不要给我们打电话我们会

打给你rdquo它与调用开发人员代码的第三方代码相符这么做的主要原因是让高级组件减少

对其子系统的依赖高级组件将控制传递给低级组件低级组件自己决定如何运行与何时

响应例如命令行程序与包含用户界面窗口的程序就不相同命令行程序停下来要求用

户输入而在包含用户界面窗口的程序中用户只须单击按钮再由窗口管理器调用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

3

有些框架(例如 Zend Framework 或 CodeIgniter)遵循松散耦合的体系结构也就是说

它们的组件彼此很少相互依赖甚至可以单独使用这更像库的风格虽然松散耦合的框

架提供的开发速度不及紧密耦合的体系结构和模型-视图-控制器(Model-View-Controller

MVC)模式所提供的速度但这种方法更加灵活代码的可控性也更强

112 使用框架的时机

框架并不能解决所有编程问题抛开今天开发的狂热状态你应该还记得几年前是如

何创建框架的当时大多数框架几乎可以说是无法有效利用的垃圾人们创建这些框架只

是为了加速开发过程根本没有关注文档优雅性易用性或代码的可读性另一群人使

用了这些代码并给它们填充了一些与原始代码几乎不相符的其他功能为了便于使用

必须清除代码但这不是完全重写也不是将代码包装到其他包装类里因为这样只会进

一步增加代码的复杂性

当然今天框架的杂乱无序不像以前那样明显因为代码的质量已经大大提高但这

既是大多数框架存在性能问题的原因也是不易掌握它们的原因同时是新框架掩盖旧框

架弱点的原因最后这也是主流框架提供全新 20 版本的原因这个版本解决了前面提

到的所有问题

1 优点

在下列情况下适合使用 Web 应用程序框架

几乎包含动态内容的所有标准项目例如社交网联机商店新闻门户网站等

易于扩展的应用程序这些应用程序可以从头发展为全球普及服务而不需要对代

码做大的变更

生成连续的应用程序其中代码(例如控制器和视图)的模块性和可重用性都非常

有用

包含最后期限员工流动和不固定客户的现实开发

如果你是专业Web 开发人员或者希望成为专业Web开发人员就必须学会如何使

用框架

这些情况适用于大多数商业 Web应用程序这些应用程序连接到数据库并允许用户

创建和修改数据库中的内容因此在 Web 开发领域中使用 Web 应用程序框架编程已

成为标准实践

2 缺点

在下列情况下应该考虑不使用框架进行开发

不包含用户创建内容的纯粹提供信息的网页例如包含奇妙图形的艺术作品

包含有限数据库连接的小项目它们不能从框架的代码生成中受益

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

4

需要提供高性能的大项目例如 Google 套件(可以使用经过编译的编程语言而不

是 PHP)

需要提供最好的性能却只包含有限硬件资源的项目(因为现在编程成本总是高于硬

件成本)

专业或实验性应用程序它们可能涉及完全未知的方向或使用某些自定义解决方

案例如用于科学试验的界面(包含面向对象数据库)

当真正需要(并能提供)完全控制代码和应用程序的演变时

当你想创建一个Web应用程序而你的合伙人不想或者甚至不会使用框架时

这些条件通常体现在以下 3种类型的项目中小型静态网站非常专业的网站和失败

的网站创建框架是为了使用标准体系结构开发常见的 Web应用程序当然可以使用插

件或模块来扩展它们但是完全更改其结构则要付出艰辛努力而且需要不断检查它们的

功能与项目的设计需求是否一致

113 PHP 与其他编程语言

多年以来PHP 一直是一种非常普及的编程语言但人们普遍认为它不够专业墨守

陈规的 PHP 开发人员都是生产廉价低质量代码而又未受过良好教育的自由人专家们推

荐使用 ZopeASP 或者各种 Java技术2005年Ruby得到了迅猛发展每个人对这种编

程语言的优雅性都惊叹不已用 Ruby 语言编写的软件 Ruby on Rails 被认为是最完美的

Web应用程序框架不久Ruby on Rails的克隆版开始涌现同时也出现了 Python的 Django

和 Turbogears以及各种 PHP 框架

PHP5 于 2004年发布这是一种面向对象的简洁编程语言如果有人写的老式 HTML

仍然混合了一些 PHP 脚本片段那么这只是他自己的选择并不是编程语言的问题一段

时间之后人们才逐渐将 PHP 作为一种规范的专业工具使用PHP 和现代MVC 范例以及

其他框架功能一起工作成为了开发 Web应用程序的首选语言

多年之后Ruby on Rails 出现了许多限制其中一个重要限制就是低可用性和 Ruby

托管的高成本而 PHP 有许多廉价的托管有许多团体希望开发早期的 PHP 框架这导

致了一场 IT 革命这场革命颠覆了 Ruby on Rails 作为最受欢迎框架的地位而用 PHP 框

架取而代之

图 1-1显示的是各种框架在 Google 搜索引擎的 Computers amp Electronics类别中搜索量

随时间的变化情况该图是使用 Google Insights for Search 创建的它是有名的 Google

Trends工具的高级形式可以在 wwwgooglecominsightssearch网站上自己搜索这些条目

查看 2011年的搜索结果

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

5

图 1-1 不同编程语言中框架的搜索量

12 开源 PHP Web 框架

另一个需要回答的问题是为什么选择这 3种特定框架它们是否真的更好还是我们

有偏见又或者是推广它们有某种商业利益先回答最后一个问题我们是完全独立的开

源拥护者我们只比较免费(就像免费演讲一样)软件因此我们背后没有利益集团也没

有人告诉我们应该选择哪种框架下面几节将回答它们是否比其他框架更好这个问题

121 公众关注的框架对比

我们之所以选择 SymfonyCakePHP 和 Zend Framework是因为它们在 Web 开发团

队中非常普及并且我们有使用 PHP 的经验我们认为开源编程工具在普及度和质量之

间至少有某种相关性只有它们有用人们才会使用就这一点而言它们不同于专有软

件或流行音乐专有软件或音乐可能通过迅猛的市场营销来实现普及性从而轻易地取代

人们对质量的关注

之前曾经也有过闭源 PHP框架但由于免费框架取得了广泛成功现在闭

源框架已经成为了过去

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

6

事实证明可以更为客观地反映公众对Web框架的兴趣图 1-2显示的是Google Insights

for Search中不同 PHP 框架的搜索量从中我们可以看出主要有 4个竞争者其他 Web框

架加起来还不及这 4个框架中的一个Lithium和 Prado 框架直接被省略了因为它们的名

称不是唯一的容易产生混淆我们在特定类别中搜索过这两个名称发现它们并不是重

要的搜索项

图 1-2 不同 PHP 框架的搜索量对比

用户搜索与框架相关的信息时结果通常是各种博客和论坛上的讨论情况以及关于

如何学习这种技术的有关条目和关于如何使用这些框架开发应用程序的相关条目由此我

们可以看出公众关注的是如何真正长期使用某种 Web 框架

对于我们而言真正有争议的是 CodeIgniter框架关于是否将它作为主要框架之一

我们争论了很长一段时间也许现在它的搜索频率与 Symfony 和 CakePHP 框架相当但

更重要的是图 1-2 中下面的区域它反映有多少人找到了答案并可能在他们的项目中使用

这种方法

当然图 1-2 中的曲线图只反映搜索量就这种增长曲线来看很难区分长期趋势和

临时现象我们知道 CodeIgniter框架非常好因此它绝对不只是一种潮流也许一两年之

后它将在主要Web工具中占有一席之地

最后我们决定讨论 3种框架但我们没有完全放弃对 CodeIgniter框架的讨论在附录

B 中我们将对它的功能进行描述其中也将介绍 Lithium 和 Agavi 框架在该附录中还将

使用每种框架开发一个简单的应用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

7

122 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息它们的网站上也只有营销性

描述在它们的网站上只是列举了这 3种框架的某些相似功能

Symfony 框架是一种全栈框架是一种用 PHP 语言编写的关联类库它为开发人

员提供了一种体系结构以及一些组件和工具以方便他们更快地构建复杂的 Web

应用程序选择 symfony 框架能让你更早发布应用程序托管升级以及维护它们

都没有问题Symfony框架并不是完全重新创造的而是基于经验的它使用大多

数Web开发的最佳实践同时集成某些第三方库

CakePHP 框架是一种快速开发框架它为开发维护和部署应用程序提供了一种可

扩展的体系结构通过在配置范例约定内使用常见的设计模式(例如MVC和ORM)

CakePHP 框架可以降低开发成本让开发人员写更少的代码

Zend Framework 是 PHP 的扩展它基于简单性面向对象的最佳实践公司友好

的许可和经过严格测试的敏捷代码库Zend Framework 致力于构建更安全更可靠

更现代的Web 20 应用程序和Web服务

你是否能够说出这三者之间的不同网站并没有提供这些框架功能的信息在各种博

客和论坛上虽然可以找到一些信息但缺少经过验证的数据而且一般讨论倾向于交流个

人观点

这就是我们写这本书的原因实际上框架之间的不同并不明显需要用一段时间对

它们进行验证也需要用一些实际示例来进行说明然后才能推广为商业解决方案下面

从最基础的内容开始

1 Symfony

开始时间2005年

许可证MIT

PHP 版本

Symfony 14 PHP 524+

Symfony 20 PHP 53+

其徽标如图 1-3 所示网址是 wwwsymfony-

projectorg

Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建开发人员是 Fabien

Potencier它最初用于开发该公司自己的应用程序2005年发布为一个开源项目其名称

是ldquosymfonyrdquo但有时为了醒目起见而将首字母大写(本书就是这么做的)

Symfony框架基于古老的Mojavi MVC 框架必然也受到 Ruby on Rails 的一些影响

它集成了 Propel 对象-关系映射器并且将 YAML Ainrsquot 标记语言(YAML Ainrsquot Markup

LanguageYAML)系列标准用于配置和数据建模默认的对象-关系映射(ORM)解决方案后

来演变成了 Doctrine

图 1-3 Symfony徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 3: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

3

有些框架(例如 Zend Framework 或 CodeIgniter)遵循松散耦合的体系结构也就是说

它们的组件彼此很少相互依赖甚至可以单独使用这更像库的风格虽然松散耦合的框

架提供的开发速度不及紧密耦合的体系结构和模型-视图-控制器(Model-View-Controller

MVC)模式所提供的速度但这种方法更加灵活代码的可控性也更强

112 使用框架的时机

框架并不能解决所有编程问题抛开今天开发的狂热状态你应该还记得几年前是如

何创建框架的当时大多数框架几乎可以说是无法有效利用的垃圾人们创建这些框架只

是为了加速开发过程根本没有关注文档优雅性易用性或代码的可读性另一群人使

用了这些代码并给它们填充了一些与原始代码几乎不相符的其他功能为了便于使用

必须清除代码但这不是完全重写也不是将代码包装到其他包装类里因为这样只会进

一步增加代码的复杂性

当然今天框架的杂乱无序不像以前那样明显因为代码的质量已经大大提高但这

既是大多数框架存在性能问题的原因也是不易掌握它们的原因同时是新框架掩盖旧框

架弱点的原因最后这也是主流框架提供全新 20 版本的原因这个版本解决了前面提

到的所有问题

1 优点

在下列情况下适合使用 Web 应用程序框架

几乎包含动态内容的所有标准项目例如社交网联机商店新闻门户网站等

易于扩展的应用程序这些应用程序可以从头发展为全球普及服务而不需要对代

码做大的变更

生成连续的应用程序其中代码(例如控制器和视图)的模块性和可重用性都非常

有用

包含最后期限员工流动和不固定客户的现实开发

如果你是专业Web 开发人员或者希望成为专业Web开发人员就必须学会如何使

用框架

这些情况适用于大多数商业 Web应用程序这些应用程序连接到数据库并允许用户

创建和修改数据库中的内容因此在 Web 开发领域中使用 Web 应用程序框架编程已

成为标准实践

2 缺点

在下列情况下应该考虑不使用框架进行开发

不包含用户创建内容的纯粹提供信息的网页例如包含奇妙图形的艺术作品

包含有限数据库连接的小项目它们不能从框架的代码生成中受益

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

4

需要提供高性能的大项目例如 Google 套件(可以使用经过编译的编程语言而不

是 PHP)

需要提供最好的性能却只包含有限硬件资源的项目(因为现在编程成本总是高于硬

件成本)

专业或实验性应用程序它们可能涉及完全未知的方向或使用某些自定义解决方

案例如用于科学试验的界面(包含面向对象数据库)

当真正需要(并能提供)完全控制代码和应用程序的演变时

当你想创建一个Web应用程序而你的合伙人不想或者甚至不会使用框架时

这些条件通常体现在以下 3种类型的项目中小型静态网站非常专业的网站和失败

的网站创建框架是为了使用标准体系结构开发常见的 Web应用程序当然可以使用插

件或模块来扩展它们但是完全更改其结构则要付出艰辛努力而且需要不断检查它们的

功能与项目的设计需求是否一致

113 PHP 与其他编程语言

多年以来PHP 一直是一种非常普及的编程语言但人们普遍认为它不够专业墨守

陈规的 PHP 开发人员都是生产廉价低质量代码而又未受过良好教育的自由人专家们推

荐使用 ZopeASP 或者各种 Java技术2005年Ruby得到了迅猛发展每个人对这种编

程语言的优雅性都惊叹不已用 Ruby 语言编写的软件 Ruby on Rails 被认为是最完美的

Web应用程序框架不久Ruby on Rails的克隆版开始涌现同时也出现了 Python的 Django

和 Turbogears以及各种 PHP 框架

PHP5 于 2004年发布这是一种面向对象的简洁编程语言如果有人写的老式 HTML

仍然混合了一些 PHP 脚本片段那么这只是他自己的选择并不是编程语言的问题一段

时间之后人们才逐渐将 PHP 作为一种规范的专业工具使用PHP 和现代MVC 范例以及

其他框架功能一起工作成为了开发 Web应用程序的首选语言

多年之后Ruby on Rails 出现了许多限制其中一个重要限制就是低可用性和 Ruby

托管的高成本而 PHP 有许多廉价的托管有许多团体希望开发早期的 PHP 框架这导

致了一场 IT 革命这场革命颠覆了 Ruby on Rails 作为最受欢迎框架的地位而用 PHP 框

架取而代之

图 1-1显示的是各种框架在 Google 搜索引擎的 Computers amp Electronics类别中搜索量

随时间的变化情况该图是使用 Google Insights for Search 创建的它是有名的 Google

Trends工具的高级形式可以在 wwwgooglecominsightssearch网站上自己搜索这些条目

查看 2011年的搜索结果

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

5

图 1-1 不同编程语言中框架的搜索量

12 开源 PHP Web 框架

另一个需要回答的问题是为什么选择这 3种特定框架它们是否真的更好还是我们

有偏见又或者是推广它们有某种商业利益先回答最后一个问题我们是完全独立的开

源拥护者我们只比较免费(就像免费演讲一样)软件因此我们背后没有利益集团也没

有人告诉我们应该选择哪种框架下面几节将回答它们是否比其他框架更好这个问题

121 公众关注的框架对比

我们之所以选择 SymfonyCakePHP 和 Zend Framework是因为它们在 Web 开发团

队中非常普及并且我们有使用 PHP 的经验我们认为开源编程工具在普及度和质量之

间至少有某种相关性只有它们有用人们才会使用就这一点而言它们不同于专有软

件或流行音乐专有软件或音乐可能通过迅猛的市场营销来实现普及性从而轻易地取代

人们对质量的关注

之前曾经也有过闭源 PHP框架但由于免费框架取得了广泛成功现在闭

源框架已经成为了过去

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

6

事实证明可以更为客观地反映公众对Web框架的兴趣图 1-2显示的是Google Insights

for Search中不同 PHP 框架的搜索量从中我们可以看出主要有 4个竞争者其他 Web框

架加起来还不及这 4个框架中的一个Lithium和 Prado 框架直接被省略了因为它们的名

称不是唯一的容易产生混淆我们在特定类别中搜索过这两个名称发现它们并不是重

要的搜索项

图 1-2 不同 PHP 框架的搜索量对比

用户搜索与框架相关的信息时结果通常是各种博客和论坛上的讨论情况以及关于

如何学习这种技术的有关条目和关于如何使用这些框架开发应用程序的相关条目由此我

们可以看出公众关注的是如何真正长期使用某种 Web 框架

对于我们而言真正有争议的是 CodeIgniter框架关于是否将它作为主要框架之一

我们争论了很长一段时间也许现在它的搜索频率与 Symfony 和 CakePHP 框架相当但

更重要的是图 1-2 中下面的区域它反映有多少人找到了答案并可能在他们的项目中使用

这种方法

当然图 1-2 中的曲线图只反映搜索量就这种增长曲线来看很难区分长期趋势和

临时现象我们知道 CodeIgniter框架非常好因此它绝对不只是一种潮流也许一两年之

后它将在主要Web工具中占有一席之地

最后我们决定讨论 3种框架但我们没有完全放弃对 CodeIgniter框架的讨论在附录

B 中我们将对它的功能进行描述其中也将介绍 Lithium 和 Agavi 框架在该附录中还将

使用每种框架开发一个简单的应用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

7

122 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息它们的网站上也只有营销性

描述在它们的网站上只是列举了这 3种框架的某些相似功能

Symfony 框架是一种全栈框架是一种用 PHP 语言编写的关联类库它为开发人

员提供了一种体系结构以及一些组件和工具以方便他们更快地构建复杂的 Web

应用程序选择 symfony 框架能让你更早发布应用程序托管升级以及维护它们

都没有问题Symfony框架并不是完全重新创造的而是基于经验的它使用大多

数Web开发的最佳实践同时集成某些第三方库

CakePHP 框架是一种快速开发框架它为开发维护和部署应用程序提供了一种可

扩展的体系结构通过在配置范例约定内使用常见的设计模式(例如MVC和ORM)

CakePHP 框架可以降低开发成本让开发人员写更少的代码

Zend Framework 是 PHP 的扩展它基于简单性面向对象的最佳实践公司友好

的许可和经过严格测试的敏捷代码库Zend Framework 致力于构建更安全更可靠

更现代的Web 20 应用程序和Web服务

你是否能够说出这三者之间的不同网站并没有提供这些框架功能的信息在各种博

客和论坛上虽然可以找到一些信息但缺少经过验证的数据而且一般讨论倾向于交流个

人观点

这就是我们写这本书的原因实际上框架之间的不同并不明显需要用一段时间对

它们进行验证也需要用一些实际示例来进行说明然后才能推广为商业解决方案下面

从最基础的内容开始

1 Symfony

开始时间2005年

许可证MIT

PHP 版本

Symfony 14 PHP 524+

Symfony 20 PHP 53+

其徽标如图 1-3 所示网址是 wwwsymfony-

projectorg

Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建开发人员是 Fabien

Potencier它最初用于开发该公司自己的应用程序2005年发布为一个开源项目其名称

是ldquosymfonyrdquo但有时为了醒目起见而将首字母大写(本书就是这么做的)

Symfony框架基于古老的Mojavi MVC 框架必然也受到 Ruby on Rails 的一些影响

它集成了 Propel 对象-关系映射器并且将 YAML Ainrsquot 标记语言(YAML Ainrsquot Markup

LanguageYAML)系列标准用于配置和数据建模默认的对象-关系映射(ORM)解决方案后

来演变成了 Doctrine

图 1-3 Symfony徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 4: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

4

需要提供高性能的大项目例如 Google 套件(可以使用经过编译的编程语言而不

是 PHP)

需要提供最好的性能却只包含有限硬件资源的项目(因为现在编程成本总是高于硬

件成本)

专业或实验性应用程序它们可能涉及完全未知的方向或使用某些自定义解决方

案例如用于科学试验的界面(包含面向对象数据库)

当真正需要(并能提供)完全控制代码和应用程序的演变时

当你想创建一个Web应用程序而你的合伙人不想或者甚至不会使用框架时

这些条件通常体现在以下 3种类型的项目中小型静态网站非常专业的网站和失败

的网站创建框架是为了使用标准体系结构开发常见的 Web应用程序当然可以使用插

件或模块来扩展它们但是完全更改其结构则要付出艰辛努力而且需要不断检查它们的

功能与项目的设计需求是否一致

113 PHP 与其他编程语言

多年以来PHP 一直是一种非常普及的编程语言但人们普遍认为它不够专业墨守

陈规的 PHP 开发人员都是生产廉价低质量代码而又未受过良好教育的自由人专家们推

荐使用 ZopeASP 或者各种 Java技术2005年Ruby得到了迅猛发展每个人对这种编

程语言的优雅性都惊叹不已用 Ruby 语言编写的软件 Ruby on Rails 被认为是最完美的

Web应用程序框架不久Ruby on Rails的克隆版开始涌现同时也出现了 Python的 Django

和 Turbogears以及各种 PHP 框架

PHP5 于 2004年发布这是一种面向对象的简洁编程语言如果有人写的老式 HTML

仍然混合了一些 PHP 脚本片段那么这只是他自己的选择并不是编程语言的问题一段

时间之后人们才逐渐将 PHP 作为一种规范的专业工具使用PHP 和现代MVC 范例以及

其他框架功能一起工作成为了开发 Web应用程序的首选语言

多年之后Ruby on Rails 出现了许多限制其中一个重要限制就是低可用性和 Ruby

托管的高成本而 PHP 有许多廉价的托管有许多团体希望开发早期的 PHP 框架这导

致了一场 IT 革命这场革命颠覆了 Ruby on Rails 作为最受欢迎框架的地位而用 PHP 框

架取而代之

图 1-1显示的是各种框架在 Google 搜索引擎的 Computers amp Electronics类别中搜索量

随时间的变化情况该图是使用 Google Insights for Search 创建的它是有名的 Google

Trends工具的高级形式可以在 wwwgooglecominsightssearch网站上自己搜索这些条目

查看 2011年的搜索结果

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

5

图 1-1 不同编程语言中框架的搜索量

12 开源 PHP Web 框架

另一个需要回答的问题是为什么选择这 3种特定框架它们是否真的更好还是我们

有偏见又或者是推广它们有某种商业利益先回答最后一个问题我们是完全独立的开

源拥护者我们只比较免费(就像免费演讲一样)软件因此我们背后没有利益集团也没

有人告诉我们应该选择哪种框架下面几节将回答它们是否比其他框架更好这个问题

121 公众关注的框架对比

我们之所以选择 SymfonyCakePHP 和 Zend Framework是因为它们在 Web 开发团

队中非常普及并且我们有使用 PHP 的经验我们认为开源编程工具在普及度和质量之

间至少有某种相关性只有它们有用人们才会使用就这一点而言它们不同于专有软

件或流行音乐专有软件或音乐可能通过迅猛的市场营销来实现普及性从而轻易地取代

人们对质量的关注

之前曾经也有过闭源 PHP框架但由于免费框架取得了广泛成功现在闭

源框架已经成为了过去

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

6

事实证明可以更为客观地反映公众对Web框架的兴趣图 1-2显示的是Google Insights

for Search中不同 PHP 框架的搜索量从中我们可以看出主要有 4个竞争者其他 Web框

架加起来还不及这 4个框架中的一个Lithium和 Prado 框架直接被省略了因为它们的名

称不是唯一的容易产生混淆我们在特定类别中搜索过这两个名称发现它们并不是重

要的搜索项

图 1-2 不同 PHP 框架的搜索量对比

用户搜索与框架相关的信息时结果通常是各种博客和论坛上的讨论情况以及关于

如何学习这种技术的有关条目和关于如何使用这些框架开发应用程序的相关条目由此我

们可以看出公众关注的是如何真正长期使用某种 Web 框架

对于我们而言真正有争议的是 CodeIgniter框架关于是否将它作为主要框架之一

我们争论了很长一段时间也许现在它的搜索频率与 Symfony 和 CakePHP 框架相当但

更重要的是图 1-2 中下面的区域它反映有多少人找到了答案并可能在他们的项目中使用

这种方法

当然图 1-2 中的曲线图只反映搜索量就这种增长曲线来看很难区分长期趋势和

临时现象我们知道 CodeIgniter框架非常好因此它绝对不只是一种潮流也许一两年之

后它将在主要Web工具中占有一席之地

最后我们决定讨论 3种框架但我们没有完全放弃对 CodeIgniter框架的讨论在附录

B 中我们将对它的功能进行描述其中也将介绍 Lithium 和 Agavi 框架在该附录中还将

使用每种框架开发一个简单的应用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

7

122 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息它们的网站上也只有营销性

描述在它们的网站上只是列举了这 3种框架的某些相似功能

Symfony 框架是一种全栈框架是一种用 PHP 语言编写的关联类库它为开发人

员提供了一种体系结构以及一些组件和工具以方便他们更快地构建复杂的 Web

应用程序选择 symfony 框架能让你更早发布应用程序托管升级以及维护它们

都没有问题Symfony框架并不是完全重新创造的而是基于经验的它使用大多

数Web开发的最佳实践同时集成某些第三方库

CakePHP 框架是一种快速开发框架它为开发维护和部署应用程序提供了一种可

扩展的体系结构通过在配置范例约定内使用常见的设计模式(例如MVC和ORM)

CakePHP 框架可以降低开发成本让开发人员写更少的代码

Zend Framework 是 PHP 的扩展它基于简单性面向对象的最佳实践公司友好

的许可和经过严格测试的敏捷代码库Zend Framework 致力于构建更安全更可靠

更现代的Web 20 应用程序和Web服务

你是否能够说出这三者之间的不同网站并没有提供这些框架功能的信息在各种博

客和论坛上虽然可以找到一些信息但缺少经过验证的数据而且一般讨论倾向于交流个

人观点

这就是我们写这本书的原因实际上框架之间的不同并不明显需要用一段时间对

它们进行验证也需要用一些实际示例来进行说明然后才能推广为商业解决方案下面

从最基础的内容开始

1 Symfony

开始时间2005年

许可证MIT

PHP 版本

Symfony 14 PHP 524+

Symfony 20 PHP 53+

其徽标如图 1-3 所示网址是 wwwsymfony-

projectorg

Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建开发人员是 Fabien

Potencier它最初用于开发该公司自己的应用程序2005年发布为一个开源项目其名称

是ldquosymfonyrdquo但有时为了醒目起见而将首字母大写(本书就是这么做的)

Symfony框架基于古老的Mojavi MVC 框架必然也受到 Ruby on Rails 的一些影响

它集成了 Propel 对象-关系映射器并且将 YAML Ainrsquot 标记语言(YAML Ainrsquot Markup

LanguageYAML)系列标准用于配置和数据建模默认的对象-关系映射(ORM)解决方案后

来演变成了 Doctrine

图 1-3 Symfony徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 5: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

5

图 1-1 不同编程语言中框架的搜索量

12 开源 PHP Web 框架

另一个需要回答的问题是为什么选择这 3种特定框架它们是否真的更好还是我们

有偏见又或者是推广它们有某种商业利益先回答最后一个问题我们是完全独立的开

源拥护者我们只比较免费(就像免费演讲一样)软件因此我们背后没有利益集团也没

有人告诉我们应该选择哪种框架下面几节将回答它们是否比其他框架更好这个问题

121 公众关注的框架对比

我们之所以选择 SymfonyCakePHP 和 Zend Framework是因为它们在 Web 开发团

队中非常普及并且我们有使用 PHP 的经验我们认为开源编程工具在普及度和质量之

间至少有某种相关性只有它们有用人们才会使用就这一点而言它们不同于专有软

件或流行音乐专有软件或音乐可能通过迅猛的市场营销来实现普及性从而轻易地取代

人们对质量的关注

之前曾经也有过闭源 PHP框架但由于免费框架取得了广泛成功现在闭

源框架已经成为了过去

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

6

事实证明可以更为客观地反映公众对Web框架的兴趣图 1-2显示的是Google Insights

for Search中不同 PHP 框架的搜索量从中我们可以看出主要有 4个竞争者其他 Web框

架加起来还不及这 4个框架中的一个Lithium和 Prado 框架直接被省略了因为它们的名

称不是唯一的容易产生混淆我们在特定类别中搜索过这两个名称发现它们并不是重

要的搜索项

图 1-2 不同 PHP 框架的搜索量对比

用户搜索与框架相关的信息时结果通常是各种博客和论坛上的讨论情况以及关于

如何学习这种技术的有关条目和关于如何使用这些框架开发应用程序的相关条目由此我

们可以看出公众关注的是如何真正长期使用某种 Web 框架

对于我们而言真正有争议的是 CodeIgniter框架关于是否将它作为主要框架之一

我们争论了很长一段时间也许现在它的搜索频率与 Symfony 和 CakePHP 框架相当但

更重要的是图 1-2 中下面的区域它反映有多少人找到了答案并可能在他们的项目中使用

这种方法

当然图 1-2 中的曲线图只反映搜索量就这种增长曲线来看很难区分长期趋势和

临时现象我们知道 CodeIgniter框架非常好因此它绝对不只是一种潮流也许一两年之

后它将在主要Web工具中占有一席之地

最后我们决定讨论 3种框架但我们没有完全放弃对 CodeIgniter框架的讨论在附录

B 中我们将对它的功能进行描述其中也将介绍 Lithium 和 Agavi 框架在该附录中还将

使用每种框架开发一个简单的应用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

7

122 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息它们的网站上也只有营销性

描述在它们的网站上只是列举了这 3种框架的某些相似功能

Symfony 框架是一种全栈框架是一种用 PHP 语言编写的关联类库它为开发人

员提供了一种体系结构以及一些组件和工具以方便他们更快地构建复杂的 Web

应用程序选择 symfony 框架能让你更早发布应用程序托管升级以及维护它们

都没有问题Symfony框架并不是完全重新创造的而是基于经验的它使用大多

数Web开发的最佳实践同时集成某些第三方库

CakePHP 框架是一种快速开发框架它为开发维护和部署应用程序提供了一种可

扩展的体系结构通过在配置范例约定内使用常见的设计模式(例如MVC和ORM)

CakePHP 框架可以降低开发成本让开发人员写更少的代码

Zend Framework 是 PHP 的扩展它基于简单性面向对象的最佳实践公司友好

的许可和经过严格测试的敏捷代码库Zend Framework 致力于构建更安全更可靠

更现代的Web 20 应用程序和Web服务

你是否能够说出这三者之间的不同网站并没有提供这些框架功能的信息在各种博

客和论坛上虽然可以找到一些信息但缺少经过验证的数据而且一般讨论倾向于交流个

人观点

这就是我们写这本书的原因实际上框架之间的不同并不明显需要用一段时间对

它们进行验证也需要用一些实际示例来进行说明然后才能推广为商业解决方案下面

从最基础的内容开始

1 Symfony

开始时间2005年

许可证MIT

PHP 版本

Symfony 14 PHP 524+

Symfony 20 PHP 53+

其徽标如图 1-3 所示网址是 wwwsymfony-

projectorg

Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建开发人员是 Fabien

Potencier它最初用于开发该公司自己的应用程序2005年发布为一个开源项目其名称

是ldquosymfonyrdquo但有时为了醒目起见而将首字母大写(本书就是这么做的)

Symfony框架基于古老的Mojavi MVC 框架必然也受到 Ruby on Rails 的一些影响

它集成了 Propel 对象-关系映射器并且将 YAML Ainrsquot 标记语言(YAML Ainrsquot Markup

LanguageYAML)系列标准用于配置和数据建模默认的对象-关系映射(ORM)解决方案后

来演变成了 Doctrine

图 1-3 Symfony徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 6: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

6

事实证明可以更为客观地反映公众对Web框架的兴趣图 1-2显示的是Google Insights

for Search中不同 PHP 框架的搜索量从中我们可以看出主要有 4个竞争者其他 Web框

架加起来还不及这 4个框架中的一个Lithium和 Prado 框架直接被省略了因为它们的名

称不是唯一的容易产生混淆我们在特定类别中搜索过这两个名称发现它们并不是重

要的搜索项

图 1-2 不同 PHP 框架的搜索量对比

用户搜索与框架相关的信息时结果通常是各种博客和论坛上的讨论情况以及关于

如何学习这种技术的有关条目和关于如何使用这些框架开发应用程序的相关条目由此我

们可以看出公众关注的是如何真正长期使用某种 Web 框架

对于我们而言真正有争议的是 CodeIgniter框架关于是否将它作为主要框架之一

我们争论了很长一段时间也许现在它的搜索频率与 Symfony 和 CakePHP 框架相当但

更重要的是图 1-2 中下面的区域它反映有多少人找到了答案并可能在他们的项目中使用

这种方法

当然图 1-2 中的曲线图只反映搜索量就这种增长曲线来看很难区分长期趋势和

临时现象我们知道 CodeIgniter框架非常好因此它绝对不只是一种潮流也许一两年之

后它将在主要Web工具中占有一席之地

最后我们决定讨论 3种框架但我们没有完全放弃对 CodeIgniter框架的讨论在附录

B 中我们将对它的功能进行描述其中也将介绍 Lithium 和 Agavi 框架在该附录中还将

使用每种框架开发一个简单的应用程序

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

7

122 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息它们的网站上也只有营销性

描述在它们的网站上只是列举了这 3种框架的某些相似功能

Symfony 框架是一种全栈框架是一种用 PHP 语言编写的关联类库它为开发人

员提供了一种体系结构以及一些组件和工具以方便他们更快地构建复杂的 Web

应用程序选择 symfony 框架能让你更早发布应用程序托管升级以及维护它们

都没有问题Symfony框架并不是完全重新创造的而是基于经验的它使用大多

数Web开发的最佳实践同时集成某些第三方库

CakePHP 框架是一种快速开发框架它为开发维护和部署应用程序提供了一种可

扩展的体系结构通过在配置范例约定内使用常见的设计模式(例如MVC和ORM)

CakePHP 框架可以降低开发成本让开发人员写更少的代码

Zend Framework 是 PHP 的扩展它基于简单性面向对象的最佳实践公司友好

的许可和经过严格测试的敏捷代码库Zend Framework 致力于构建更安全更可靠

更现代的Web 20 应用程序和Web服务

你是否能够说出这三者之间的不同网站并没有提供这些框架功能的信息在各种博

客和论坛上虽然可以找到一些信息但缺少经过验证的数据而且一般讨论倾向于交流个

人观点

这就是我们写这本书的原因实际上框架之间的不同并不明显需要用一段时间对

它们进行验证也需要用一些实际示例来进行说明然后才能推广为商业解决方案下面

从最基础的内容开始

1 Symfony

开始时间2005年

许可证MIT

PHP 版本

Symfony 14 PHP 524+

Symfony 20 PHP 53+

其徽标如图 1-3 所示网址是 wwwsymfony-

projectorg

Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建开发人员是 Fabien

Potencier它最初用于开发该公司自己的应用程序2005年发布为一个开源项目其名称

是ldquosymfonyrdquo但有时为了醒目起见而将首字母大写(本书就是这么做的)

Symfony框架基于古老的Mojavi MVC 框架必然也受到 Ruby on Rails 的一些影响

它集成了 Propel 对象-关系映射器并且将 YAML Ainrsquot 标记语言(YAML Ainrsquot Markup

LanguageYAML)系列标准用于配置和数据建模默认的对象-关系映射(ORM)解决方案后

来演变成了 Doctrine

图 1-3 Symfony徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 7: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

7

122 3 种框架概述

框架概述几乎没有给我们提供关于框架功能的任何信息它们的网站上也只有营销性

描述在它们的网站上只是列举了这 3种框架的某些相似功能

Symfony 框架是一种全栈框架是一种用 PHP 语言编写的关联类库它为开发人

员提供了一种体系结构以及一些组件和工具以方便他们更快地构建复杂的 Web

应用程序选择 symfony 框架能让你更早发布应用程序托管升级以及维护它们

都没有问题Symfony框架并不是完全重新创造的而是基于经验的它使用大多

数Web开发的最佳实践同时集成某些第三方库

CakePHP 框架是一种快速开发框架它为开发维护和部署应用程序提供了一种可

扩展的体系结构通过在配置范例约定内使用常见的设计模式(例如MVC和ORM)

CakePHP 框架可以降低开发成本让开发人员写更少的代码

Zend Framework 是 PHP 的扩展它基于简单性面向对象的最佳实践公司友好

的许可和经过严格测试的敏捷代码库Zend Framework 致力于构建更安全更可靠

更现代的Web 20 应用程序和Web服务

你是否能够说出这三者之间的不同网站并没有提供这些框架功能的信息在各种博

客和论坛上虽然可以找到一些信息但缺少经过验证的数据而且一般讨论倾向于交流个

人观点

这就是我们写这本书的原因实际上框架之间的不同并不明显需要用一段时间对

它们进行验证也需要用一些实际示例来进行说明然后才能推广为商业解决方案下面

从最基础的内容开始

1 Symfony

开始时间2005年

许可证MIT

PHP 版本

Symfony 14 PHP 524+

Symfony 20 PHP 53+

其徽标如图 1-3 所示网址是 wwwsymfony-

projectorg

Symfony 框架由一家名为 Sensio Labs 的法国 Web 开发公司创建开发人员是 Fabien

Potencier它最初用于开发该公司自己的应用程序2005年发布为一个开源项目其名称

是ldquosymfonyrdquo但有时为了醒目起见而将首字母大写(本书就是这么做的)

Symfony框架基于古老的Mojavi MVC 框架必然也受到 Ruby on Rails 的一些影响

它集成了 Propel 对象-关系映射器并且将 YAML Ainrsquot 标记语言(YAML Ainrsquot Markup

LanguageYAML)系列标准用于配置和数据建模默认的对象-关系映射(ORM)解决方案后

来演变成了 Doctrine

图 1-3 Symfony徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 8: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

8

今天的 Symfony框架是主要 Web框架之一它有一个非常活跃的大型社区还有许多

文档mdashmdash主要是免费的电子书2010 年末发布的 Symfony 20 提供了更多新功能性能也

大大增强

2 CakePHP

开始时间2005年

许可证MIT

PHP 版本432+

其徽标如图 1-4所示网址是 httpcakephporg

2005 年波兰 Web 开发人员 Micha Tatarynowicz 提出了

CakePHP 框架受 Ruby on Rails 的影响CakePHP框架是一个

完全由社区驱动的开源项目其主要开发人员是 Larry Masters(也

称为 PhpNut)下一个版本的 CakePHP 框架已经宣布但发布日

期尚未确定

CakePHP 框架最重要的特性是其友好性开发速度和易用性在这些方面它的确胜人

一筹该框架开箱即用无须配置其文档非常完美并且包含很多用于说明大部分功能

的运行示例它的确有许多好的功能支持使用更少数量的代码来实现更快的开发速度

CakePHP框架最有争议的功能之一是它与 PHP4兼容它曾经允许部署在不支持 PHP5

的主机上现在这变成了阻碍 CakePHP 框架发展的障碍幸运的是版本 20将使用 PHP

53+还有一些报告提到了 CakePHP 框架的不良性能但这些不良性能主要是由默认的无

效缓存造成的

3 Zend Framework

开始时间2005年

许可证新 BSD

PHP 版本从 ZF 170 后使用的是 PHP 524

其徽标如图 1-5所示网址是 httpframework zendcom

Zend Framework 由一家美国-以色列合资公司 Zend

Technologies Ltd 创建这家合资公司由 Andi Gutmans

和 Zeev Suraski联合创建这两个人是 PHP 的核心开发

人员Zend Technologies Ltd 的战略伙伴包括 Adobe

IBMGoogle和Microsoft该公司还提供各种商业产品

然而Zend Framework 是在ldquo公司友好rdquo的新 BSD 许

可下发布的开源项目

ZF是简单的基于组件的和松散耦合的这意味着它是一个可以随意使用的组件库

可以选择使用 MVC 体系结构这样能够降低学习难度增加灵活性因为这种框架全面

图 1-4 CakePHP 徽标

图 1-5 Zend Framework 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 9: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

9

面向对象而且经过单元测试所以文档非常优秀源码质量非常高Zend也宣布即将发

布 20版本但发布日期尚未确定

123 其他框架

PHP 框架有几百种如果你仔细数一下就会发现这个数字并不夸张这些包括原来

的和已经取消的项目以及新启动的项目和一些无用的短期项目Web应用程序市场是一

个庞大的市场而 PHP 工具的数量也很多在某种程度上说甚至是过多下面简单介绍一

些比较有名的框架有人已经使用这些框架成功开发出一些 Web应用程序

1 CodeIgniter

开始时间2006年

许可证修订后的 BSD

PHP 版本432+

其徽标如图 1-6所示网址是 httpcodeignitercom

CodeIgniter 框架由一家私有软件开发公司 Ellis Labs 负责

开发和维护它虽从小处着眼但对性能提升很大它部分采

用 MVC 模式因此模型是可选的这种框架是松散耦合的

用 Rasmus Lerdorf的话说就是ldquo它最不像框架rdquo其轻量级方法

在开发人员社区中赢得了广泛认同但有时也有人批评它与

PHP4 兼容

对于需要使用某种框架而又不太复杂的 Web 应用程序而

言CodeIgniter 框架是一个不错的选择而对于更复杂的 Web 应用程序因为功能过多

或者因为需要花费过多时间来进行配置所以使用该框架将影响应用程序的性能

CodeIgniter框架结构简单因此许多初学者都把它作为学习完整MVC框架之前的学习平台

2 Lithium

开始时间2009年

许可证BSD

PHP 版本53+

其徽标如图 1-7所示网址是 httplithifyme

图 1-7 Lithium徽标

Lithium 框架借鉴了 CakePHP 的所有优点并将它移植到 PHP 53 上最初它是

CakePHP 的一个分支名为 Cake3现在它是一个由 CakePHP 以前的开发人员运行的单独

图 1-6 CodeIgniter 徽标

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 10: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

10

项目它是轻量级的非常快速和灵活并且广泛支持插件同时它还具有许多实验性

和创新性的功能例如过滤器系统和集成测试套件

在 Google 中搜索ldquoLithium 框架rdquo显示的第二个搜索结果就是标题为ldquoCakePHP is

deadhellipLithium was bornrdquo的页面然而这种说法并不准确因为 Lithium框架支持 PHP 53

凭借这一优势Lithium框架将来也许真的会超过 CakePHP除非 CakePHP 立即采取行动

3 Agavi

开始时间2005年

许可证LGPL

PHP 版本520+(推荐使用 528+)

其徽标如图 1-8所示网址为 wwwagaviorg

Agavi框架和 Symfony框架一样都是基于Mojavi 框架的该

框架于 2005 年启动直到 2009 年早期才发布 100 版它的源代

码非常完美因此有时也称为写得最好的 MVC OOP 框架然而

由于缺乏文档这种框架并没有得到普及

这种框架原本就没有打算普及它的作者们强调说 Agavi 框架

不是构建网站的工具箱而是一种具有强大性能和可扩展性的重要

框架其目标应用程序是需要完全控制开发人员的长期专业项目

4 Kohana

开始时间2007年

许可证BSD

PHP 版本523+

其徽标如图 1-9所示网址为 httpkohanaphpcom

Kohana 框架是 CodeIgniter 支持社区的一个分支与

CodeIgniter相比Kohana 框架是为 PHP5 而设计的并且完全

面向对象虽然其代码以更简洁著称但它还是拥有 CodeIgniter

的全部特性它是轻量级的非常灵活容易掌握Kohana框架背后的社区非常庞大也

非常活跃因此虽然这种框架还很年轻但它是一种稳定可信赖的框架

5 Prado

开始时间2004年

许可证经过修订的 BSD

PHP 版本510+

其徽标如图 1-10所示网址是 wwwpradosoftcom

Prado是面向对象的 PHP快速应用程序开发(PHP Rapid Application

Development Object-oriented)的简称一段时间以前它还比较普及

图 1-8 Agavi 徽标

图 1-9 Kohana 徽标

图 1-10 Prado 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 11: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

11

但现在发展有些迟缓然而它仍然是一种适用于大多数商业应用程序的成熟框架它最

有意义的功能之一是非常支持事件驱动编程并且与 ASPNET 也有一些相似之处

6 Yii

开始时间2008年

许可证BSD

PHP 版本510+

其徽标如图 1-11所示网址是 wwwyiiframeworkcom

图 1-11 Yii 徽标

Yii 框架由 Prado 的开发人员创建并且延续了它的许多约定Yii 框架是一种非常快

速(超过大多数基准)可扩展模块化且严格面向对象的框架它的功能众多文档完美

它没有使用特殊配置或者模板语言因此如果要使用它那么除了面向对象 PHP 之外不

需要学习其他任何语言和其他许多框架不同它遵循纯 MVC 体系结构将数据直接从

模型(Model)发送到视图(View)

7 Akelos

开始时间2006年

许可证LGPL

PHP 版本4或 5

其徽标如图 1-12所示网址是 http wwwakelosorg 或 httpgithubcombermiakelos

图 1-12 Akelos 徽标

PHP 框架多少受到了 Ruby on Rails的影响而 Akelos 框架首当其冲它关注国际化(提

供多语言模型和视图以及没有扩展名的 Unicode支持)可以在廉价的共享托管主机上运

行(这也是它支持 PHP4 的原因)

Akelos 框架的作者们已经宣布完全重写的 Akelos 2它不再支持 PHP4并使用自动

加载和更惰性的策略来加载功能它的特点是高级路由方法和强大的 REST 定向(第 12 章

将详细介绍 REST)这种框架将在 2010 年后期发布值得期待

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 12: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

12

8 Seagull

开始时间2001年

许可证BSD

PHP 版本4311+

其徽标如图 1-13所示网址是 httpseagullprojectorg

在所有 PHP 框架中Seagull 资格最老mdashmdash 它创建于

2001 年多年的开发使得它更稳定更可靠和更经得起测试开发人员不会再积极地开发

它因此也许在开发新项目时它不是最佳选择但还是有很多成功的应用程序使用它

它对其他所有 PHP 框架的发展都做出了很大贡献

9 Qcodo

开始时间2005年

许可证MIT

PHP 版本5x

其徽标如图 1-14所示网址是 wwwqcodocom

Qcodo 框架是一种 MVC 框架在代码生成方面胜过数据

库设计它有一个功能强大的代码生成器能够分析数据模型

的结构并为数据库操作创建 PHP 对象代码和 HTML 页面

也许这种框架不是最普及的框架之一但有些顶级机构(包括 NASA)都在项目中使用它

Qcodo 框架最初由 QuasIdea Development 的Mike Ho 开发现在由一个活跃的社区开发

它还有一个完全由社区驱动的分支 Qcube

10 Solar

开始时间2005年

许可证新的 BSD

PHP 版本52+

其徽标如图 1-15所示网址是 httpsolarphpcom

图 1-15 Solar 框架徽标

SOLAR是简单对象库和应用程序仓库(Simple Object Library and Application Repository)

的简称它的结构和命名约定与 Zend Framework 类似最大的不同点之一是构造对象的方

法mdashmdash 都使用统一的构造函数创建使用配置文件中的数组进行配置它还有许多有用的

图 1-13 Seagull 徽标

图 1-14 Qcodo 徽标

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 13: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

13

内置示例应用程序

11 PHP on Trax

开始时间2007年

许可证GPL

PHP 版本5x

其徽标如图 1-16所示网址为 wwwphpontraxcom

图 1-16 PHP on Trax 徽标

顾名思义这种框架完全是 Ruby on Rails 的 PHP 副本至少原本打算这样但因此

它仍然缺少许多功能所以最终很可能会无法实现这个目标它是看起来很好但最终还是

失败的框架之一

13 Web 框架中的设计模式

有一些抽象化概念能够在应用程序之间传送从而能够加快开发过程本节将详细介

绍这些抽象化概念以及它们创造 Web应用程序框架的方法

使用框架之前不一定要理解设计模式因此如果你觉得本章没有意义那么可以跳到

下一章然而对于框架和应用程序开发整体而言设计模式还是非常重要的因此我们

认为如果现在跳过本节以后还会回到这里

131 设计模式的定义

设计模式的定义是它是软件设计中常见问题的通用解决方案其实没有规范的基础

因为设计模式通常是一种实践方式用以弥补规范机制的缺失当编程语言不能提供抽象

机制而这些机制在现实应用程序开发过程中又非常有用时通常会创造设计模式

设计模式与国际象棋游戏类似初学者只要知道规则就行了类似于学习编程语言的

基本语法当然知道ldquo象rdquo如何移动并不能使你成为优秀的象棋选手就像知道如何用

括号并不能使你成为 PHP 程序员一样熟练的选手能够预测下面几步再运用相应的取胜

方案就像经验丰富的编程人员能够创造工作软件一样

随着对象棋游戏的进一步熟悉你就会发现有一些模式你只用看一眼棋盘就知道这

种情形属于哪种模式然后依据现在的情况和未来的风险做出反应你可以直观地理解这

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 14: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

14

些模式也可以命名它们这与软件设计模式很类似如果你非常熟悉这些模式那么可

能会一直使用它们有时也会在毫无察觉的情况下使用它们

虽然不一定要命名设计模式但命名有两个好处首先它有助于认识模式因为在

命名抽象事物之后在实践中才更容易实现它然后可以进一步分析这种模式画出它的

示意图并充分利用这种模式另一个好处是这么做能够共享经验象棋选手喜欢讨论各

种开局和第一着棋编程人员也可以通过交流设计模式来相互学习

更重要的是如果要其他编程人员为固定类添加一些功能并告诉他使用 Decorator

模式那么可以用你确定的方式(而不是使用随机解决方案)来实现设计模式具有防止未

来出现问题的巨大潜力

132 模型-视图-控制器作为主要的结构设计模式

Web 框架利用了大多数(如果不是全部)设计模式但 MVC 绝对是所有框架的结构主

干其主要思想是将应用程序划分为 3层

模型层表示应用程序的业务逻辑它不仅仅是原始数据模型层必须表示数据结

构的关系和依赖性它可能包含一个或多个类这些类对应应用程序的逻辑对象

并提供一个用于操作它们的接口模型层是唯一使用永久存储的层它完全封装所

有数据库的连接当其内部状态发生改变时模型层应该通知视图层以便刷新视

图层

视图层显示给用户的输出最重要的是视图层从不修改应用程序的数据它只

显示数据对于相同的数据可能有多个视图层例如传统的 HTMLPDFFlash

或移动设备的WML这些视图层应该可以互换而不用修改其他层

控制器层应用程序负责处理用户交互和采取其他动作的部分控制器层应该很简

单mdashmdash 它是控制部分使用模型层和视图层提供的方法它自己不做任何事情

图 1-17说明了这 3层之间的关系

动作 数据操作

控制器层

控制 模型层

视图层

结果 传送数据

图 1-17 模型-视图-控制器模式

MVC 与 MVP

MVC 是一种比较老的设计模式时间可以追溯到 1979 年 Trygve Reenskaug 的著作

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 15: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

15

Applications Programming in Smalltalk-80How to use ModelndashViewndashController从那时起它

就经常在非 Web 应用程序中使用这些应用程序大多数是用像 C++或 Java 这样的编译语

言写的图形用户界面实现具体的 MVC 模式很容易也很自然但对于 Web应用程序需

要对它稍做修改

如图 1-18 所示模型-视图-表示器(Model-View-PresenterMVP)是 MVC 的派生物

它是一个 3 层的应用程序结构其中表示器层是视图层和模型层之间的中间层表示器与

控制器不同它从模型层加载数据然后将数据传递给视图层

大多数所谓的MVC 框架都遵循 MVP 模式这本身没有错因为MVP 似乎更适合

但是这种命名约定可能会产生混淆只要 MVP 直接来源于 MVC这就不是什么大问题

因此本书沿用框架作者取的这些名称我们可以将所有框架都称为模型-视图-控制器即

使由控制器层完成主要的数据传递工作

动作

更新

结果

视图层

模型层

表示器层

数据操作与传递

图 1-18 模型-视图-表示器模式

133 其他设计模式概述

设计模式可以划分为创造模式行为模式和结构模式 3种完整描述所有这些设计模

式超出了本书的范围在下面这本非常有影响力的书中可以找到相应描述Design Patterns

Elements of Reusable Object-Oriented Software(作者Erich GammaRichard HelmRalph

Johnson和 John Vlissides)下面简要介绍 Web框架中常用的一些设计模式

1 Singleton

这种设计模式通常称为反模式它很普通但非常有用Singleton模式的目的是确保类

只有一个实例并且让这个实例在全局范围内可访问当另一个对象需要访问 Singleton 模

式时它将调用一个全局可访问的静态函数来返回对单个实例的引用图 1-19 显示的是

Singleton模式的结构

图 1-19 Singleton 模式的结构

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 16: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

16

Singleton 模式的诀窍是让该实例及其所有构造函数私有化从而无法创建 Singleton

类的实例那么如何创建第一个也是唯一一个实例呢首先instance()方法检查对象是否

存在如果不存在那么在返回它之前创建一个实例下面看看使用 PHP 代码如何实现

ltphp

class CarSingleton

private $make = Dodge

private $model = Magnum

private static $car = NULL

private static $isRented = FALSE

private function __construct()

static function rentCar()

if (FALSE == self$isRented )

if (NULL == self$car)

self$car= new CarSingleton()

self$isRented = TRUE

return self$car

else

return NULL

function returnCar(CarSingleton $carReturned)

self$isRented = FALSE

function getMake() return $this-gtmake

function getModel() return $this-gtmodel

function getMakeAndModel() return $this-gtgetMake()

$this-gtgetModel()

gt

codesnippetsingletonCarSingletonclassphp

上面代码中的类是一个 Singleton模式表示汽车租赁公司 Dodge Magnum的一个具体

的汽车样本__construct()函数是这个类的构造函数请注意将它设置为 private 以防止

从类外部使用它双下划线表示__construct()是 PHP 中的魔法函数(该语言提供的特殊函数)

之一在类中声明构造函数将重写默认的构造函数

CarSingleton提供一个界面表示租车和还车以及租车者rentCar()函数首先检查汽车

是否已经租出这不是 Singleton 模式的一部分但对于本示例的逻辑非常重要如果汽车

没有租出那么该函数在返回之前先检查$car变量是否为 NULL如果该变量等于 NULL

那么在首次使用之前先构建它因此rentCar()函数对应设计模式的 instance()方法

下面示例中的 Customer 类表示享受汽车租赁公司服务的某个人他可以租车(这里只

有一辆车)还车了解正在驾驶的汽车的制造商和型号

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 17: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

17

ltphp

include_once(CarSingletonclassphp)

class Customer

private $rentedCar

private $drivesCar = FALSE

function __construct()

function rentCar()

$this-gtrentedCar = CarSingletonrentCar()

if ($this-gtrentedCar == NULL)

$this-gtdrivesCar = FALSE

else

$this-gtdrivesCar = TRUE

function returnCar()

$this-gtrentedCar-gtreturnCar($this-gtrentedCar)

function getMakeAndModel()

if (TRUE == $this-gtdrivesCar )

return I drive $this-gtrentedCar-gtgetMakeAndModel() really

fast

else

return I cant rent this car

gt

codesnippetsingletonCustomerclassphp

可以用下面的代码来测试这些类该代码创建两个客户他们同时要租车但第二个

客户只有等到第一个客户还车后才能租到车

ltphp

include_once(Customerclassphp)

$Customer_1 = new Customer()

$Customer_2 = new Customer()

echo Customer_1 wants to rent the car ltbr gt

$Customer_1-gtrentCar()

echo Customer_1 says $Customer_1-gtgetMakeAndModel() ltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car ltbr gt

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

$Customer_1-gtreturnCar()

echo Customer_1 returned the carltbr gt

echo ltbr gt

echo Customer_2 wants to rent the car Again ltbr gt

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 18: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

18

$Customer_2-gtrentCar()

echo Customer_2 says $Customer_2-gtgetMakeAndModel() ltbr gt

echo ltbr gt

gt

codesnippetsingletonTestphp

输出如下所示

Customer_1 wants to rent the car

Customer_1 says I drive Dodge Magnum really fast

Customer_2 wants to rent the car

Customer_2 says I cant rent this car

Customer_1 returned the car

Customer_2 wants to rent the car Again

Customer_2 says I drive Dodge Magnum really fast

Singleton模式通常用于其他设计模式中例如PrototypeStateAbstract Factory或Facade

除此之外还可以在所有需要全局访问的单个实例的类中使用它但无法将它指派给其他

对象也许还可以从首次使用时的初始化中受益请注意很容易过度使用 Singletons 模

式这样很危险就像过度使用全局变量一样使用 Singletons 模式的另一个问题是在程

序的整个执行过程中它们都保持状态不变这样会严重影响单元测试有些专家甚至说

Singleton模式不是一个好模式应该避免使用它

但框架使用 Singleton 模式有多种原因原因之一是出于安全的目的存储用户数据需

要一个实例来保存用户验证数据并且确保不能创建其他实例例如Symfony的 sfGuard

类就使用这种方法

2 Prototype

当需要灵活创建参数化对象但又不想使用 new操作符时就可以使用 Prototype 模式

使用抽象的 clone()方法和一些实现 clone()的子类创建一个父类这样就可以创建对象每

个子类都包含一个实例化的 Prototype对象调用新实例时它复制本身这样就很容易灵

活地创建对象mdashmdash 不需要在代码中对具体子类名称进行硬连接而且可以作为字符串或者

对相应 Prototype 的引用来传递类的名称

这种模式还非常支持对象的深度复制这种复制不是复制 Prototype而是复制现有对

象然后接收该对象的副本作为结果甚至还可以从包含各种子类的混合对象的容器中复

制对象唯一要求是它们要实现 clone()接口用这种方法复制对象比使用 new操作符并指

定值来创建对象更快这种模式的通用示意图如图 1-20所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 19: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

19

图 1-20 Prototype 模式的结构

PHP还有另一个魔法函数__clone()能够完成大多数任务在下面的示例中唯一要做

的就是为不同生产者创建一个抽象的 CarPrototype 类和子类__clone()函数声明为 abstract

类型因此当调用这种方法时默认使用子类方法

ltphp

abstract class CarPrototype

protected $model

protected $color

abstract function __clone()

function getModel()

return $this-gtmodel

function getColor()

return $this-gtcolor

function setColor($colorIn)

$this-gtcolor= $colorIn

class DodgeCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Dodge Magnum

function __clone()

class SubaruCarPrototype extends CarPrototype

function __construct()

$this-gtmodel = Subaru Outback

function __clone()

gt

codesnippetprototypeCarPrototypeclassphp

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 20: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

20

汽车就是非常好的示例因为在现实中制造商创建原型然后基于这种原型创建不同

的模型再填充特有功能下面的代码用于测试前面的类首先它创建两个 Prototype 对

象作为展示的汽车然后复制其中一个对象来满足客户并可通过统一的界面来挑选颜色

ltphp

include_once(CarPrototypeclassphp)

$dodgeProto= new DodgeCarPrototype()

$subaruProto = new SubaruCarPrototype()

echo Which car do you want ltbr gt

$customerDecision = Subaru

if( $customerDecision == Subaru )

$customersCar = clone $subaruProto

else

$customersCar = clone $dodgeProto

echo $customersCar-gtgetModel()ltbr gt

echo What color do you wantltbr gt

$customersCar-gtsetColor(red)

echo Fine we will paint your $customersCar-gtgetModel()

$customersCar-gtgetColor()ltbr gt

gt

codesnippetprototypeTestphp

前面的代码将得到下面的消息

Which car do you want

Subaru Outback

What color do you want

Fine we will paint your Subaru Outback red

通常在框架的不同模块中使用 Prototype 模式例如可以在 Symfony 或者 CakePHP

的 AppController类的表单中嵌套表单

3 Decorator

子类化是一个很好的机制但它也有一些严重的局限性假设要生产一辆汽车你努

力设计一种别人能买得起的汽车标准模型这种完整的设计定义模型的观感并且是以后

所有修改的参考然后你可以提供一些可选配置这些配置会给汽车增加一些新功能从

而提高汽车的性能例如它可能是四轮驱动而不是前轮驱动可能是自动档而不是手动

档该车可能有不同的装配级别可能包含电动皮革座椅天窗更好的音频系统或者 GPS

卫星导航系统但基本界面相同mdashmdash 你可以开这辆车并且感觉良好

如果面临这些选择就会大大增加可能的组合形式图 1-21显示 3种改进的一些组合

如继承层次结构所示

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 21: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

21

图 1-21 继承层次结构

解决这个问题的答案是 Decorator模式Decorator这个类与装饰类共享接口(在本示例

中它是包含基本配置的汽车)它封装装饰对象的实例并动态扩展其性能这就像把礼

物放在一个盒子里然后用彩纸将它包装起来一样mdashmdash 虽然它仍然是一份礼物但更耐磨

更漂亮Decorator模式的继承结构如图 1-22所示

图 1-22 Decorator 模式更合理的继承层次结构

可以毫无限制地将装饰对象放入其他 Decorator 模式中用这种方法可以随意添加多

个可选模块Decorator模式也有自身的继承层次结构在层次结构中它们能够递归封装核

心对象

下面的代码创建一个没有可选设备的标准 Car类

ltphp

class Car

public $gearMessage = Remember to shift up

public $comfortMessage = standard

function drive()

return Accelerating $this-gtgearMessage

Driving comfort is $this-gtcomfortMessage

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 22: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

22

gt

codesnippetdecoratorCarclassphp

下面的类负责扩展汽车的性能第一个类 CarDecorator 是包装的第一层它存储$car 变

量和$comfortMessage变量的副本Decorator模式会改变这个变量因此创建一个副本以免改

变原始的$car对象另一方面 $gearMessage也在内部改变子类化 drive()函数以使用合适

的变量$car-gtmodel 和$this-gtgearMessage应该访问核心对象而不是$this-gtcomfortMessage

因为要用到修改后的值

包装 CarDecorator 的第二层装饰类用来安装可选组件如下所示Automatic

TransmissionDecorator 将$gearMessage 直接安装到核心的$car 中而 GPSDecorator 安装到

CarDecorator中请注意所有装饰类都共享相同接口另外还提供具体的安装程序

ltphp

class CarDecorator

protected $car

protected $gearMessage

protected $comfortMessage

public function __construct(Car $car_in)

$this-gtcar = $car_in

$this-gtcomfortMessage = $car_in-gtcomfortMessage

function drive()

return Accelerating $this-gtcar-gtgearMessage

Driving comfort is $this-gtcomfortMessage

class AutomaticTransmissionDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installAutomaticTransmission()

$this-gtdecorator-gtcar-gtgearMessage = Auto transmission shifts

up

class GPSDecorator extends CarDecorator

protected $decorator

public function __construct(CarDecorator $decorator_in)

$this-gtdecorator= $decorator_in

public function installGPS()

$this-gtdecorator-gtcomfortMessage= very high

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 23: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

23

gt

codesnippetdecoratorCarDecoratorclassphp

使用下面的代码来测试这些类

ltphp

include_once(Carclassphp)

include_once(CarDecoratorclassphp)

$car = new Car()

$decorator = new CarDecorator($car)

$transmission = new AutomaticTransmissionDecorator($decorator)

$gps = new GPSDecorator($decorator)

echo Driving standard car ltbr gt

echo $car-gtdrive()ltbr gt

$transmission-gtinstallAutomaticTransmission()

$gps-gtinstallGPS()

echo Driving fully decorated car ltbr gt

echo $decorator-gtdrive() ltbr gt

echo Driving the car without decoration ltbr gt

echo $car-gtdrive() ltbr gt

gt

codesnippetdecoratorTestphp

结果如下所示

Driving standard car

Accelerating Remember to shift up Driving comfort is standard

Driving fully decorated car

Accelerating Auto transmission shifts up Driving comfort is very high

Driving the car without decoration

Accelerating Auto transmission shifts up Driving comfort is standard

首先调用基本的 car 模型接着安装可选配置调用 CarDecorator 的 drive()函数最

后选择开车而不使用 Decorator包装请注意调用$car之后选择的是自动档因为

Decorator模式永久性地改变了它

接着讨论框架Decorator 模式也用于其他布局和模板中当需要添加可选视觉组件或

者需要新微件来扩展用户界面时这种模式非常有用例如如果用户输入超过字段区域

就会添加滚动条

4 Chain of Responsibility

前面的 3种设计模式关注的是对象创建和继承结构Chain of Responsibility 是另一种

类型的模式因为它应用于对象的行为它的主要目的是分离请求的发送方与接收方下

面用汽车示例来说明其用法

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 24: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

24

假设道路出现紧急情况需要马上停车

换句话说发出了 stop 请求在大多数情况

下踩下刹车踏板就可以了但有时制动器

会失灵这时 Chain of Responsibility就派上

用场了如果制动器不能处理该请求就会

将它传递给手动制动器如果由于某种原因

手动制动器也失灵那么撞上障碍物之后

至少安全气囊应该弹出以保住乘客的性命

对于大多数道路紧急事件而言安全气囊是

最常见的解决方案虽然与专业解决方案(刹

车避让)相比不是最好的但如果这些解决

方案都无效安全气囊解决方案就显得尤为

重要这与应用程序是一样的mdashmdash 最好为请求提供一个潜在的处理程序链(如图 1-23所示)

而不是在它无效的时候连一条错误消息都没有

那么该如何创建这种 Chain of Responsibility呢这种模式的主要思想就是通过一系列

连续的处理程序来处理请求以避免硬件连接的映射在处理程序链中最初客户只保持

对第一个处理程序的引用每个处理程序保持对下一个处理程序的引用最后一个处理程

序必须总是接受请求以避免将它传递给 NULL值

支持这种行为模式的类结构如图 1-24所示它由一个父 Handler类组成该父类通过

调用 handle()方法将请求委托给下一个具体的处理程序 nextHandler具体的处理程序子类

化 Handler 类这些具体的处理程序会处理请求如果它们处理失败则调用它们超类的

handle()方法

图 1-24 Chain of Responsibility模式结构

Chain of Responsibility通常用于过滤器处理用户请求就是其中一个过滤的示例首

先检查给定的控制器是否存在如果不存在则显示 404 error如果存在则将请求传递

给控制器它进一步处理请求它检查用户是否试图访问不安全的页面如果是那么它

将请求重定向到一个安全的 SSL页面然后检查身份验证等

客户

处理元素

处理元素

处理元素

处理元素

请求

图 1-23 Chain of Responsibility 作为请求的响应

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 25: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

第 1 章 SymfonyCakePHP 和 Zend Framework 简介

25

5 State

有时我们希望组件根据应用程序的各种可能状态做出不同响应首先定义一个抽象的

State类它是各种 ConcreteStates 的通用接口所有状态都提供一种 handle()方法这种方

法提供组件的各种行为Context类是包装 ConcreteState state对象的核心类如果 Context

类是提供状态独立功能的完整类这种设计模式就非常有用否则子类化 Context 类可

能更有效

在处理它自己的请求时Context类调用 state-gthandle()方法Context类还有一些方法

能够切换状态依据 state 变量存放的 ConcreteState 不同state-gthandle()方法提供不同的行

为可以将它用于模拟运行时部分类型的改变其示意图如图 1-25所示

图 1-25 state 模式结构

State模式虽然很简单但对于应用程序开发来说却非常有用例如数据库连接mdashmdash 数

据库抽象层可能依据当前的连接状态改变行为再例如联机商店的交易状态应用程序可

以根据需要哪些步骤完成交易来显示不同的页面

6 Iterator

有许多类型的集合对象也有许多方法能够遍历它们例如由连续整数遍历的数组

可应用于数组运算符如果要打印出包含 5个元素的 myArray那么可以使用下面的代码

for ($i=0$ilt=4$i++)

echo $myArray[$i]

但是这种解决方案并不完美首先必须确定变量 i 的值PHP 不是 CC++因此调用

myArray[100]并不是灾难性的mdashmdash 它不会从内存返回随机垃圾数据然而它仍然很容易跳

过某些硬连接范围内的值另一个问题是这种方法显示了聚集的基本表示它使得这种

遍历过程依赖具体的表示因此不可重用面向对象编程的目的就是封装聚集对象的内部

结构并且只提供一个统一安全的有用接口就像 PHP 提供的接口一样

interface Iterator

function current() Returns the value of element under current key

function key() Returns the current key

function next() Moves the internal pointer to the next element

function rewind() Moves the internal pointer to the first element

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容

Page 26: Symfony CakePHP 和Zend Framework 简介images.china-pub.com/ebook55001-60000/59792/ch01.pdf · 有些框架(例如Zend Framework 或CodeIgniter)遵循松散耦合的体系结构,也就是说,

PHP 框架高级编程mdashmdash应用 SymfonyCakePHP 和 Zend

26

function valid() Returns true if the element under current key is valid

现在每个实现这种接口的类都可以使用 foreach 结构下面的代码片段产生的输出与

前面的 for循环一样

foreach ($myArray as $value)

echo $value

这种机制背后的抽象化内容是 Iterator设计模式如图 1-26所示Client应用程序能够

访问两个抽象类Collection 和 TraversalAbstraction前者是聚集对象接口后者是由相应

的 Collection 创建的对于 List和Map而言具体聚集可能不同但可以为它们生成相应

的遍历方法Client 调用 next()方法为 List 和 Map 执行不同的排序算法但在这两种情

况下将会出现并发元素

图 1-26 Iterator 模式结构

在 Web 框架中Iterator 模式主要用于分页需要一个统一界面将 Web 内容划分到足

够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容