Upload
others
View
7
Download
0
Embed Size (px)
Citation preview
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
第 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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容
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 内容划分到足
够多的页面中再将它们转换到单独网页中然后通过这些页面遍历内容