http://www.ibm.com/developerworks/cn/opensource/os-php-designpatterns/
简介: PHP V5 的面向对象特性使您能够实现设计模式来改进代码设计。通过这种方式改进代码设计,代码在进行修改时将变得更加易读、更易维护且更加健壮。
设计模式 一书介绍了很多此类概念。当时,我还在学习面向对象 (OO),因此我发现那本书中有许多概念都很难领会。但是,随着越来越熟悉 OO 概念 —— 尤其是接口和继承的使用 —— 我开始看到设计模式中的实际价值。作为一名应用程序开发人员,即使从不了解任何模式或者如何及何时使用这些模式,对您的职业生涯也没有什么大的影响。但 是,我发现了解这些模式以及 developerWorks 文章 “五种常见 PHP 设计模式” 中介绍的那些模式的优秀知识后(请参阅 参考资料),您可以完成两件事情:
有句谚语说得好:“当您手中拿着一把锤子时,所有事物看上去都像钉子”。当您认为自己找到一个优秀模式时,您可能会尝试到处使用它,即使在不应当使用它的位置。记住您必须考虑正在学习的模式的使用目的,不要为了使用模式而把这些模式强行应用到应用程序的各个部分中。
本文将介绍可用于改进 PHP 代码的五个模式。每个模式都将介绍一个特定场景。可以在 下载 部分中获得这些模式的 PHP 代码。
要发挥本文的最大功效并使用示例,需要在计算机中安装以下软件:
- PHP V5 或更高版本(本文是使用 PHP V5.2.4 撰写的)
- 压缩程序,例如 WinZIP(用于压缩可下载的代码归档)
注:虽然您也可以使用纯文本编辑器,但是我发现拥有语法高亮显示和语法纠错功能的编辑器真的很有帮助。本文中的示例是使用 Eclipse PHP Development Tools (PDT) 编写的。
在需要将一类对象转换成另一类对象时,请使用适配器模式。通常,开发人员通过一系列赋值代码来处理此过程,如清单 1 所示。适配器模式是整理此类代码并在其他位置重用所有赋值代码的优秀方法。此外,它还将隐藏赋值代码,如果同时还要设定格式,这样可以极大地简化工作。
class AddressDisplay { private $addressType; private $addressText; public function setAddressType($addressType) { $this->addressType = $addressType; } public function getAddressType() { return $this->addressType; } public function setAddressText($addressText) { $this->addressText = $addressText; } public function getAddressText() { return $this->addressText; } } class EmailAddress { private $emailAddress; public function getEmailAddress() { return $this->emailAddress; } public function setEmailAddress($address) { $this->emailAddress = $address; } } $emailAddress = new EmailAddress(); /* Populate the EmailAddress object */ $address = new AddressDisplay(); /* Here's the assignment code, where I'm assigning values from one object to another... */ $address->setAddressType("email"); $address->setAddressText($emailAddress->getEmailAddress()); |
此示例将使用 AddressDisplay
对象把地址显示给用户。AddressDisplay
对象有两部分:地址类型和一个格式化的地址字符串。
在实现模式(参见清单 2)后,PHP 脚本将不再需要担心如何把 EmailAddress
对象转换成 AddressDisplay
对象。那是件好事,尤其是在 AddressDisplay
对象发生更改时或者控制如何把 EmailAddress
对象转换成 AddressDisplay
对象的规则发生更改时。记住,以模块化风格设计代码的主要优点之一就是,在业务领域发生一些更改时或者需要向软件中添加新功能时尽可能少的使用更改。即使在执行普通任务(例如把一个对象的属性值赋给另一个对象)时,也请考虑使用此模式。
class EmailAddressDisplayAdapter extends AddressDisplay { public function __construct($emailAddr) { $this->setAddressType("email"); $this->setAddressText($emailAddr->getEmailAddress()); } } $email = new EmailAddress(); $email->setEmailAddress("user@example.com"); $address = new EmailAddressDisplayAdapter($email); echo($address->getAddressType() . "\n") ; echo($address->getAddressText()); |
图 1 显示了适配器模式的类图。
编写适配器的替代方法 —— 并且是推荐方法 —— 是实现一个接口来修改行为,而不是扩展对象。这是一种非常干净的、创建适配器的方法并且没有扩展对象的缺点。使用接口的缺点之一是需要把实现添加到适配器类中,如图 2 所示:
迭代器模式将提供一种通过对象集合或对象数组封装迭代的方法。如果需要遍历集合中不同类型的对象,则使用这种模式尤为便利。
查看上面清单 1 中的电子邮件和物理地址示例。在添加迭代器模式之前,如果要遍历个人地址,则可能要遍历物理地址并显示这些地址,然后遍历个人电子邮件地址并显示这些地址,然后遍历个人 IM 地址并显示这些地址。非常复杂的遍历!
相反,通过实现迭代器,您只需要调用 while($itr->hasNext())
并处理下一个条目 $itr->next()
返回。清单 3 中显示了一个迭代器示例。迭代器功能强大,因为您可以添加要遍历的新类型条目,并且无需更改遍历条目的代码。例如,在 Person
示例中,可以添加 IM 地址数组;只需更新迭代器,无需更改遍历地址的任何代码。
class PersonAddressIterator implements AddressIterator { private $emailAddresses; private $physicalAddresses; private $position; public function __construct($emailAddresses) { $this->emailAddresses = $emailAddresses; $this->position = 0; } public function hasNext() { if ($this->position >= count($this->emailAddresses) || $this->emailAddresses[$this->position] == null) { return false; } else { return true; } } public function next() { $item = $this->emailAddresses[$this->position]; $this->position = $this->position + 1; return $item; } } |
如果把 Person
对象修改为返回 AddressIterator
接口的实现,则在将实现扩展为遍历附加对象时无需修改使用迭代器的应用程序代码。您可以使用一个混合迭代器,它封装了遍历清单 3 中列出的每种地址的迭代器。本文提供了此类应用示例(请参阅 下载)。
图 3 显示了迭代器模式的类图。
考虑清单 4 中的代码样例。这段代码的目的是要把许多功能添加到 Build Your Own Car 站点的汽车中。每个汽车模型都有更多功能及相关价格。如果只针对两个模型,使用 if then
语句添加这些功能十分平常。但是,如果出现了新模型,则必须返回查看代码并确保语句对新模型工作正常。
require('classes.php'); $auto = new Automobile(); $model = new BaseAutomobileModel(); $model = new SportAutomobileModel($model); $model = new TouringAutomobileModel($model); $auto->setModel($model); $auto->printDescription(); |
进入装饰器模式,该模式允许您通过一个优秀整洁的类将此功能添加到 AutomobileModel
。每个类仅仅关注其价格、选项以及添加到基本模型的方式。
图 4 显示了装饰器模式的类图。
装饰器模式的优点是可以轻松地同时跟踪库的多个装饰器。
如果您拥有流对象的使用经验,则一定使用过装饰器。大多数流结构(例如输出流)都是接受基本输入流的装饰器,然后通过添加附加功能来装饰它 —— 例如从文件输入流、从缓冲区输入流,等等。
委托模式将提供一种基于各种条件委托行为的方法。考虑清单 5 中的代码。这段代码包含几个条件。根据条件,代码将选择相应类型的对象来处理请求。
pkg = new Package("Heavy Package"); $pkg->setWeight(100); if ($pkg->getWeight() > 99) { echo( "Shipping " . $pkg->getDescription() . " by rail."); } else { echo("Shipping " . $pkg->getDescription() . " by truck"); } |
使用委托模式,对象将内在化(internalize)此发送过程,方法为在调用如清单 6 中的 useRail()
之类的方法时设置对相应对象的内部引用。如果处理各个包的条件发生更改或者使用新的送货类型时,则使用此模式尤为便利。
require_once('classes.php'); $pkg = new Package("Heavy Package"); $pkg->setWeight(100); $shipper = new ShippingDelegate(); if ($pkg->getWeight() > 99) { $shipper->useRail(); } $shipper->deliver($pkg); |
委托将通过调用 useRail()
或 useTruck()
方法来切换处理工作的类,从而提供动态更改行为的优点。
图 5 显示了委托模式的类图。
状态模式类似于命令模式,但是意图截然不同。考虑下面的代码。
class Robot { private $state; public function powerUp() { if (strcmp($state, "poweredUp") == 0) { echo("Already powered up...\n"); /* Implementation... */ } else if ( strcmp($state, "powereddown") == 0) { echo("Powering up now...\n"); /* Implementation... */ } } public function powerDown() { if (strcmp($state, "poweredUp") == 0) { echo("Powering down now...\n"); /* Implementation... */ } else if ( strcmp($state, "powereddown") == 0) { echo("Already powered down...\n"); /* Implementation... */ } } /* etc... */ } |
在此清单中,PHP 代码表示变成一辆汽车的强大机器人的操作系统。机器人可以启动、关闭、由汽车变成机器人以及由机器人变成汽车。代码现已就绪,但是您会看到如果任何规则发生更改或者添加另一个状态则会变得十分复杂。
现在查看清单 8,其中提供了相同的逻辑处理机器人的状态,但是这一次把逻辑放入状态模式。清单 8 中的代码完成的工作与初始代码相同,但是用于处理状态的逻辑已经被放入每个状态的一个对象中。为了演示使用设计模式的优点,假定不久以后,这些机器人发现 它们不应在处于机器人模式时关闭。实际上,如果它们关闭,它们必须先切换到汽车模式。如果它们已经处于汽车模式下,则机器人将关闭。使用状态模式,对代码 的更改十分微小。
$robot = new Robot(); echo("\n"); $robot->powerUp(); echo("\n"); $robot->turnIntoRobot(); echo("\n"); $robot->turnIntoRobot(); /* This one will just give me a message */ echo("\n"); $robot->turnIntoVehicle(); echo("\n"); |
class NormalRobotState implements RobotState { private $robot; public function __construct($robot) { $this->robot = $robot; } public function powerUp() { /* implementation... */ } public function powerDown() { /* First, turn into a vehicle */ $this->robot->setState(new VehicleRobotState($this->robot)); $this->robot->powerDown(); } public function turnIntoVehicle() { /* implementation... */ } public function turnIntoRobot() { /* implementation... */ } } |
图 6 中一个不太明显的地方就是状态模式中的每个对象都有对上下文对象(机器人)的引用,因此每个对象都可以把状态提升到相应的状态。
在 PHP 代码中使用设计模式可以使代码更容易阅读、更易维护。通过使用已经建立的模式,您将从通用的设计结构中获益,从而允许团队的其他开发人员了解代码的意图。它还使您可以从其他设计者完成的工作中获益,因此无需从失败的设计理念中吸取教训
相关推荐
本文实例讲述了PHP设计模式之装饰器模式定义与用法。分享给大家供大家参考,具体如下: 什么是装饰器模式 作为一种结构型模式, 装饰器(Decorator)模式就是对一个已有结构增加”装饰”. 适配器模式, 是为现在有结构...
本文实例讲述了php设计模式之单例模式。分享给大家供大家参考。具体分析如下: 单例模式(职责模式): 简单的说,一个对象(在学习设计模式之前,需要比较了解面向对象思想)只负责一个特定的任务; 单例类: 1、...
本文实例讲述了PHP设计模式:原型模式Prototype。分享给大家供大家参考,具体如下: 1. 概述 我们都知道,创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以...
本文实例讲述了PHP设计模式之装饰器模式。分享给大家供大家参考,具体如下: 装饰器模式又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也...
本文实例讲述了PHP设计模式之适配器模式(Adapter)原理与用法。分享给大家供大家参考,具体如下: 这个适配器模式,就是为了将一个类的接口转换成客户希望的另外一个接口,并且使用原本不兼容的而不能在一起工作的...
PEAR创始人Stig Saether Bakken,PHP核心贡献者Derick Rethans三大高手合力而作:本书几乎囊括了PHP 5所有的新特性,包括PHP 5所有的新功能,PHP 5的面向对象编程方法和设计模式,以及PHP 5的新的数据库连接处理、...
本文实例讲述了PHP设计模式之装饰器模式定义与用法。分享给大家供大家参考,具体如下: 装饰器模式: 如果已有对象的部分内容或功能性发生改变,但是不需要修改原始对象的结构或不使用继承,动态的扩展一个对象的...
本文实例讲述了PHP设计模式:适配器模式Adapter。分享给大家供大家参考,具体如下: 1. 概述: 接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题。程序提供者们修改他们的代码;系统库被...
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原来由于接口不兼容而不能一起工作的那此类可以一起工作 二、适配器模式结构图 三、适配器模式中主要角色 目标(Target)角色:定义客户端使用的与特定...
本文实例讲述了php设计模式之模板模式。分享给大家供大家参考,具体如下: 星际中的虫族部队有个特别的进化兵种,就是飞龙,飞龙可以变成空中卫士(天蟹)或者吞噬者(对空的)。另外还有口水兵可以进化变成地刺。 ...
PEAR创始人Stig Saether Bakken,PHP核心贡献者Derick Rethans三大高手合力而作:本书几乎囊括了PHP 5所有的新特性,包括PHP 5所有的新功能,PHP 5的面向对象编程方法和设计模式,以及PHP 5的新的数据库连接处理、...
设计模式-创新-ObjectPool 对象组模式是一种软件创作设计模式,用于初始化类实例的成本非常高的情况。 基本上,对象组是一个包含一定数量对象的容器。 因此,从池中删除对象时,在将对象放回之前,该对象在池中不...
php /** * 适配器模式 * * 将一个类的接口转换成客户希望的另外一个接口,使用原本不兼容的而不能在一起工作的那些类可以在一起工作 */ // 这个是原有的类型 class OldCache { public function __construct() { echo ...
在开发大型系统时,往往会出现这样一种情况: 我有一部分基础数据,是类classA是从数据库A读取出来的,其他很多的...工厂模式是一种类,建立了一个工厂来根据所需来创建对象,这种方式在多态性编程中是很重要的,允许动
另外,在查询、过滤等应用场合中,通过预定义多个条件,然后使用这些条件的组合来处理查询或过滤,而不是使用逻辑判断语句来处理,可以简化整个实现逻辑。 这里的每个条件就是一个规格,多个规格/条件通过串联的方式...
本书的核心内容包括:PHP基础语法、函数、面向对象编程、PHP类、常见的设计模式、正则表达式、PHP操作图像和文件、MVC架构思想、ThinkPHP框架、NoSQL与MySQL等。另外,还介绍了当前热点的O2O网站开发和App后台开发的...
可将一个类的接口转换成客户希望的另外一个接口,使得原本不兼容的接口能够一起工作。通俗的理解就是将不同接口适配成统一的API接口。 角色: Target适配目标,该角色定义把其他类转换为何种接口,也就是我们的期望...
本系统主要有管理人员用户和学生用户两个子系统,系统管理员可以设置教师管理 ...另外 B/S 模式的实现,也使得考试更加 方便,客户端只要安装浏览器就能登录考试,不受地域限制,不同地方的考生可以同时 参加考试。
内容涵盖了动态网站开发的前端技术(HTML5)、企业现在主流应用版本PHP 5.4为主的语法、PHP的常用功能模块和实用技巧、MySQL数据库的设计与应用、PHP 面向对象的程序设计思想、数据库抽象层PDO、Smarty模板技术、Web...