PHP设计模式(五)适配器模式Adapter实例详解【结构型】 |
本文实例讲述了PHP设计模式:适配器模式Adapter 。分享给大家供大家参考,具体如下: 1. 概述:接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题 。程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相关库的发展和进化 。 例子1:iphone4,你即可以使用UBS接口连接电脑来充电,假如只有iphone没有电脑,怎么办呢?苹果提供了iphone电源适配器 。可以使用这个电源适配器充电 。这个iphone的电源适配器就是类似我们说的适配器模式 。(电源适配器就是把电源变成需要的电压,也就是适配器的作用是使得一个东西适合另外一个东西 。) 例子2:最典型的例子就是很多功能手机,每一种机型都自带有从电器,有一天自带充电器坏了,而且市场没有这类型充电器可买了 。怎么办?万能充电器就可以解决 。这个万能充电器就是适配器 。 2. 问题你如何避免因外部库的API改变而带来的不便?假如你写了一个库,你能否提供一种方法允许你软件的现有用户进行完美地升级,即使你已经改变了你的API?为了更好地适宜于你的需要,你应该如何改变一个对象的接口? 3. 解决方案适配器(Adapter)模式为对象提供了一种完全不同的接口 。你可以运用适配器(Adapter)来实现一个不同的类的常见接口,同时避免了因升级和拆解客户代码所引起的纠纷 。 适配器模式(Adapter Pattern),把一个类的接口变换成客户端所期待的另一种接口, Adapter模式使原本因接口不匹配(或者不兼容)而无法在一起工作的两个类能够在一起工作 。又称为转换器模式、变压器模式、包装(Wrapper)器模式(把已有的一些类包装起来,使之能有满足需要的接口) 。 4. 分类共有两类适配器模式:1.类的适配器模式(采用继承实现)2.对象适配器(采用对象组合方式实现) 1)类适配器模式 ——适配器继承自已实现的类(一般多重继承) 。 Adapter与Adaptee是继承关系 1、用一个具体的Adapter类和Target进行匹配 。结果是当我们想要一个匹配一个类以及所有它的子类时,类Adapter将不能胜任工作 Adapter与Adaptee是委托关系 1、允许一个Adapter与多个Adaptee同时工作 。Adapter也可以一次给所有的Adaptee添加功能 5. 适用性以下情况使用Adapter模式: 1 • 你想使用一个已经存在的类,而它的接口不符合你的需求 。 6. 结构类适配器使用多重继承对一个接口与另一个接口进行匹配,如下图所示: 对象匹配器依赖于对象组合,如下图所示: 7. 构建模式的组成•目标角色(Target):— 定义Client使用的与特定领域相关的接口 。 8. 效果类适配器和对象适配器有不同的权衡 。 类适配器 • 用一个具体的Adapter类对Adaptee和Target进行匹配 。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作 。 对象适配器则 • 允许一个Adapter与多个Adaptee—即Adaptee本身以及它的所有子类(如果有子类的话)—同时工作 。Adapter也可以一次给所有的Adaptee添加功能 。 使用Adapter模式时需要考虑的其他一些因素有: 1) Adapter的匹配程度 对Adaptee的接口与Target的接口进行匹配的工作量各个Adapter可能不一样 。工作范围可能是,从简单的接口转换(例如改变操作名 )到支持完全不同的操作集合 。Adapter的工作量取决于Target接口与Adaptee接口的相似程度 9. 实现类适配器使用的是继承 让我们看看当API改变时,如何保护应用程序不受影响 。 <?php /** * 类适配器模式 * @author guisu * */ /** * 目标角色 * @version 1.0 */ class Target { /** * 这个方法将来有可能改进 */ public function hello(){ echo 'Hello '; } /** * 目标点 */ public function world(){ echo 'world'; } } /** * Client 程序 * */ class Client { /** * Main program. */ public static function main() { $Target = new Target(); $Target->hello(); $Target->world(); } } Client::main(); ?> 我们Target已经明确指出hello()方法会在未来的版本中改进,甚至不被支持或者淘汰 。接下来,现在假设第二版的Target已经发布 。一个全新的greet()方法代替了hello() 。 <?php /** * 类适配器模式 * @author guisu * */ /** * 目标角色 * @version 2.0 */ class Target { /** * 这个方法将来有可能继续改进 */ public function greet(){ echo 'Greet '; } /** * 目标点 */ public function world(){ echo 'world'; } } 如果我们继续使用原来的client代码,肯定会报错,找不到hello方法 。 针对API“升级”的解决办法就是创建一个适配器(Adapter) 。 类适配器使用的是继承: <?php /** * 类适配器模式 * @author guisu * */ /** * 目标角色 * @version 2.0 */ interface Target { /** * 源类的方法:这个方法将来有可能继续改进 */ public function hello(); /** * 目标点 */ public function world(); } /** * 源角色:被适配的角色 */ class Adaptee { /** * 源类含有的方法 */ public function world() { echo ' world <br />'; } /** * 加入新的方法 */ public function greet() { echo ' Greet '; } } /** * 类适配器角色 */ class Adapter extends Adaptee implements Target { /** * 源类中没有world方法,在此补充 */ public function hello() { parent::greet(); } } /** * 客户端程序 * */ class Client { /** * Main program. */ public static function main() { $adapter = new Adapter(); $adapter->hello(); $adapter->world(); } } Client::main(); ?> 对象适配器使用的是委派 <?php /** * 类适配器模式 * @author guisu * */ /** * 目标角色 * @version 2.0 */ interface Target { /** * 源类的方法:这个方法将来有可能继续改进 */ public function hello(); /** * 目标点 */ public function world(); } /** * 源角色:被适配的角色 */ class Adaptee { /** * 源类含有的方法 */ public function world() { echo ' world <br />'; } /** * 加入新的方法 */ public function greet() { echo ' Greet '; } } /** * 类适配器角色 */ class Adapter implements Target { private $_adaptee; /** * construct * * @param Adaptee $adaptee */ public function __construct(Adaptee $adaptee) { $this->_adaptee = $adaptee; } /** * 源类中没有world方法,在此补充 */ public function hello() { $this->_adaptee->greet(); } /** * 源类中没有world方法,在此补充 */ public function world() { $this->_adaptee->world(); } } /** * 客户端程序 * */ class Client { /** * Main program. */ public static function main() { $adaptee = new Adaptee(); $adapter = new Adapter($adaptee); $adapter->hello(); $adapter->world(); } } Client::main(); ?> 如例中代码所示,你可以运用适配器(Adapter)模式来避免因外部库改变所带来的不便——倘若向上兼容 。作为某个库的开发者,你应该独立编写适配器,使你的用户更简便地使用新版本的库,而不用去修改他们现有的全部代码 。 GoF书中提出的适配器(Adapter)模式更倾向于运用继承而不是组成 。这在强类型语言中是有利的,因为适配器(Adapter)事实上是一个目标类的子类,因而能更好地与类中方法相结合 。 了更好的灵活性,我个人比较倾向于组成的方法(特别是在结合了依赖性倒置的情况下);尽管如此,继承的方法提供两种版本的接口,或许在你的实际运用中反而是一个提高灵活性的关键 。 10.适配器模式与其它相关模式桥梁模式(bridge模式):桥梁模式与对象适配器类似,但是桥梁模式的出发点不同:桥梁模式目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立的加以改变 。而对象适配器模式则意味着改变一个已有对象的接口 装饰器模式(decorator模式):装饰模式增强了其他对象的功能而同时又不改变它的接口 。因此装饰模式对应用的透明性比适配器更好 。结果是decorator模式支持递归组合,而纯粹使用适配器是不可能实现这一点的 。 Facade(外观模式):适配器模式的重点是改变一个单独类的API 。Facade的目的是给由许多对象构成的整个子系统,提供更为简洁的接口 。而适配器模式就是封装一个单独类,适配器模式经常用在需要第三方API协同工作的场合,设法把你的代码与第三方库隔离开来 。 适配器模式与外观模式都是对现相存系统的封装 。但这两种模式的意图完全不同,前者使现存系统与正在设计的系统协同工作而后者则为现存系统提供一个更为方便的访问接口 。简单地说,适配器模式为事后设计,而外观模式则必须事前设计,因为系统依靠于外观 。总之,适配器模式没有引入新的接口,而外观模式则定义了一个全新的接口 。 代理模式(Proxy )在不改变它的接口的条件下,为另一个对象定义了一个代理 。 装饰者模式,适配器模式,外观模式三者之间的区别: 装饰者模式的话,它并不会改变接口,而是将一个一个的接口进行装饰,也就是添加新的功能 。 适配器模式是将一个接口通过适配来间接转换为另一个接口 。 外观模式的话,其主要是提供一个整洁的一致的接口给客户端 。 更多关于PHP相关内容感兴趣的读者可查看本站专题:《php面向对象程序设计入门教程》、《PHP数组(Array)操作技巧大全》、《PHP基本语法入门教程》、《PHP运算与运算符用法总结》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》 希望本文所述对大家PHP程序设计有所帮助 。 |