PHP设计模式(五)适配器模式Adapter实例详解【结构型】

本文实例讲述了PHP设计模式:适配器模式Adapter。分享给大家供大家参考,具体如下:

1. 概述:

接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题。程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相关库的发展和进化。

例子1:iphone4,你即可以使用UBS接口连接电脑来充电,假如只有iphone没有电脑,怎么办呢?苹果提供了iphone电源适配器。可以使用这个电源适配器充电。这个iphone的电源适配器就是类似我们说的适配器模式。(电源适配器就是把电源变成需要的电压,也就是适配器的作用是使得一个东西适合另外一个东西。)

例子2:最典型的例子就是很多功能手机,每一种机型都自带有从电器,有一天自带充电器坏了,而且市场没有这类型充电器可买了。怎么办?万能充电器就可以解决。这个万能充电器就是适配器。

2. 问题

你如何避免因外部库的API改变而带来的不便?假如你写了一个库,你能否提供一种方法允许你软件的现有用户进行完美地升级,即使你已经改变了你的API?为了更好地适宜于你的需要,你应该如何改变一个对象的接口?

3. 解决方案

适配器(Adapter)模式为对象提供了一种完全不同的接口。你可以运用适配器(Adapter)来实现一个不同的类的常见接口,同时避免了因升级和拆解客户代码所引起的纠纷。

适配器模式(Adapter Pattern),把一个类的接口变换成客户端所期待的另一种接口, Adapter模式使原本因接口不匹配(或者不兼容)而无法在一起工作的两个类能够在一起工作。又称为转换器模式、变压器模式、包装(Wrapper)器模式(把已有的一些类包装起来,使之能有满足需要的接口)。
     考虑一下当(不是假设!)一个第三方库的API改变将会发生什么。过去你只能是咬紧牙关修改所有的客户代码,而情况往往还不那么简单。你可能正从事一项新的项目,它要用到新版本的库所带来的特性,但你已经拥有许多旧的应用程序,并且它们与以前旧版本的库交互运行地很好。你将无法证明这些新特性的利用价值,如果这次升级意味着将要涉及到其它应用程序的客户代码。

4. 分类

共有两类适配器模式:1.类的适配器模式(采用继承实现)2.对象适配器(采用对象组合方式实现)

1)类适配器模式    ——适配器继承自已实现的类(一般多重继承)。

Adapter与Adaptee是继承关系

1、用一个具体的Adapter类和Target进行匹配。结果是当我们想要一个匹配一个类以及所有它的子类时,类Adapter将不能胜任工作
2、使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子集
3、仅仅引入一个对象,并不需要额外的指针以间接取得adaptee
2)对象适配器模式—— 适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。

Adapter与Adaptee是委托关系

1、允许一个Adapter与多个Adaptee同时工作。Adapter也可以一次给所有的Adaptee添加功能
2、使用重定义Adaptee的行为比较困难
无论哪种适配器,它的宗旨都是:保留现有类所提供的服务,向客户提供接口,以满足客户的期望。
即在不改变原有系统的基础上,提供新的接口服务。

5. 适用性

以下情况使用Adapter模式:

1 • 你想使用一个已经存在的类,而它的接口不符合你的需求。
2 • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
3 •(仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。即仅仅引入一个对象,并不需要额外的指针以间接取得adaptee。

6. 结构

类适配器使用多重继承对一个接口与另一个接口进行匹配,如下图所示:

对象匹配器依赖于对象组合,如下图所示:

7. 构建模式的组成

•目标角色(Target):— 定义Client使用的与特定领域相关的接口。
• 客户角色(Client):与符合Target接口的对象协同。
• 被适配橘色(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。
• 适配器角色(Adapte) :适配器模式的核心。它将对被适配Adaptee角色已有的接口转换为目标角色Target匹配的接口。对Adaptee的接口与Target接口进行适配.

8. 效果

类适配器和对象适配器有不同的权衡。

类适配器

• 用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。
• 使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。
• 仅仅引入了一个对象,并不需要额外的指针以间接得到 Adaptee。

对象适配器则

• 允许一个Adapter与多个Adaptee—即Adaptee本身以及它的所有子类(如果有子类的话)—同时工作。Adapter也可以一次给所有的Adaptee添加功能。
• 使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。

使用Adapter模式时需要考虑的其他一些因素有:

1) Adapter的匹配程度 对Adaptee的接口与Target的接口进行匹配的工作量各个Adapter可能不一样。工作范围可能是,从简单的接口转换(例如改变操作名 )到支持完全不同的操作集合。Adapter的工作量取决于Target接口与Adaptee接口的相似程度
2) 可插入的Adapter   当其他的类使用一个类时,如果所需的假定条件越少,这个类就更具可复用性。如果将接口匹配构建为一个类,
就不需要假定对其他的类可见的是一个相同的接口。也就是说,接口匹配使得我们可以将自己的类加入到一些现有的系统中去,
而这些系统对这个类的接口可能会有所不同。 
3) 使用双向适配器提供透明操作 使用适配器的一个潜在问题是,它们不对所有的客户都透明。被适配的对象不再兼容 Adaptee的接口,
因此并不是所有 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程序设计有所帮助。

(0)

相关推荐

  • php设计模式之适配器模式实例分析【星际争霸游戏案例】

    本文实例讲述了php设计模式之适配器模式.分享给大家供大家参考,具体如下: 星际的很多兵种,都有至少一项特殊技能.而且有些兵种的技能是相同的,比如虫族部队都会恢复血. 如果按照一般的思路,把技能的操作和控制作为方法,放在每个兵种的定义类来实现,代码会重复,也不容易修改. 那我们就会考虑用继承的办法,比如我们可以设计一个虫族的基类,里面有受伤后血恢复的方法. 在设计刺蛇(Hydralisk,口水兵)的时候,我们可以让刺蛇类继承虫族基类. 但是刺蛇是可以研发钻地的,而钻地不是刺蛇独有的功能,是虫族地

  • PHP设计模式(四)原型模式Prototype实例详解【创建型】

    本文实例讲述了PHP设计模式:原型模式Prototype.分享给大家供大家参考,具体如下: 1.   概述 我们都知道,创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,这无疑是一种非常有效的方式,快速的创建一个新的对象. 例子1:孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来. 例子2:寄个快递 下面是一个邮寄快递的场景: "给我寄个快递."顾客说. &qu

  • PHP设计模式概论【概念、分类、原则等】

    本文实例讲述了PHP设计模式.分享给大家供大家参考,具体如下: 1. 设计模式 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样. 模式的经典定义:每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心,通过这种方

  • PHP设计模式之适配器模式定义与用法详解

    本文实例讲述了PHP设计模式之适配器模式定义与用法.分享给大家供大家参考,具体如下: 适配器很容易理解, 大多数人家庭都有手机转接器, 用来为移动电话充电,这就是一种适配器. 如果只有USB接头, 就无法将移动电话插到标准插座上. 实际上, 必须使用一个适配器, 一端接USB插头, 一端接插座. 当然, 你可以拿出电气工具,改装USB连接头, 或者重新安装插座, 不过这样会带来很多额外的工作, 而且可能会把连接头或插座弄坏. 所以, 最可取的方法就是找一个适配器. 软件开发也是如此. 类适配器模

  • PHP设计模式(三)建造者模式Builder实例详解【创建型】

    本文实例讲述了PHP设计模式:建造者模式Builder.分享给大家供大家参考,具体如下: 1. 概述 在软件开发的过程中,当遇到一个"复杂的对象"的创建工作,该对象由一定各个部分的子对象用一定的算法构成,由于需求的变化,复杂对象的各个部分经常面临剧烈的变化,但将它们组合在一起的算法相对稳定. 例子1:买肯德基 典型的儿童餐包括一个主食,一个辅食,一杯饮料和一个玩具(例如汉堡.炸鸡.可乐和玩具车).这些在不同的儿童餐中可以是不同的,但是组合成儿童餐的过程是相同的. 客户端:顾客,想去买一

  • PHP设计模式之适配器模式原理与用法分析

    本文实例讲述了PHP设计模式之适配器模式原理与用法.分享给大家供大家参考,具体如下: 一.什么是适配器模式 适配器模式有两种:类适配器模式和对象适配器模式.其中类适配器模式使用继承方式,而对象适配器模式使用组合方式.由于类适配器模式包含双重继承,而PHP并不支持双重继承,所以一般都采取结合继承和实现的方式来模拟双重继承,即继承一个类,同时实现一个接口.类适配器模式很简单,但是与对象适配器模式相比,类适配器模式的灵活性稍弱.采用类适配器模式时,适配器继承被适配者并实现一个接口:采用对象适配器模式时

  • PHP设计模式之适配器模式代码实例

    目标: 可将一个类的接口转换成客户希望的另外一个接口,使得原本不兼容的接口能够一起工作.通俗的理解就是将不同接口适配成统一的API接口. 角色: Target适配目标,该角色定义把其他类转换为何种接口,也就是我们的期望接口. Adaptee被适配者,就是需要被适配的接口. Adapter适配器,其他的两个角色都是已经存在的角色,而适配器角色是需要新建立的,它用来对Adaptee与Target接口进行适配. 应用场景: 如数据操作有mysql.mysqli.pdo.sqlite.postgresq

  • PHP设计模式(一)工厂模式Factory实例详解【创建型】

    本文实例讲述了PHP设计模式(一)工厂模式Factory.分享给大家供大家参考,具体如下: 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.但是在一些情况下, new操作符直接生成对象会带来一些问题.举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象. 在这些情况,新对象的建立就是一个 "过程",不仅是一个操作,像一

  • php设计模式之适配器模式原理、用法及注意事项详解

    本文实例讲述了php设计模式之适配器模式原理.用法及注意事项.分享给大家供大家参考,具体如下: 在这个有没有对象都要高呼"面向对象"的年代,掌握面向对象会给我们带来意想不到的方便.学编程的小伙伴从开始能写几行代码实现简单功能到后来懂得将一些重复的操作组合起来形成一个"函数",再到后来将"函数"和属性组合起来形成一个"类".一步步走来,我们在考虑着机器运行代码效率的提高的同时也在考虑减轻程序员的工作量. 那么我们今天讲到的适配器

  • 学习php设计模式 php实现适配器模式

    一.意图 将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原来由于接口不兼容而不能一起工作的那此类可以一起工作 二.适配器模式结构图 三.适配器模式中主要角色 目标(Target)角色:定义客户端使用的与特定领域相关的接口,这也就是我们所期待得到的 源(Adaptee)角色:需要进行适配的接口 适配器(Adapter)角色:对Adaptee的接口与Target接口进行适配:适配器是本模式的核心,适配器把源接口转换成目标接口,此角色为具体类 四.适配器模式适用场景 1.你想使用一

  • php设计模式 Adapter(适配器模式)

    复制代码 代码如下: <?php /** * 适配器模式 * * 将一个类的接口转换成客户希望的另外一个接口,使用原本不兼容的而不能在一起工作的那些类可以在一起工作 */ // 这个是原有的类型 class OldCache { public function __construct() { echo "OldCache construct<br/>"; } public function store($key,$value) { echo "OldCach

  • PHP设计模式之适配器模式(Adapter)原理与用法详解

    本文实例讲述了PHP设计模式之适配器模式(Adapter)原理与用法.分享给大家供大家参考,具体如下: 这个适配器模式,就是为了将一个类的接口转换成客户希望的另外一个接口,并且使用原本不兼容的而不能在一起工作的那些类可以在一起工作.它的核心思想就是把对某些相似的类的操作转化为一个统一的"接口"(这里是比喻的说话)--适配器,或者比喻为一个"界面",统一或屏蔽了那些类的细节.适配器模式还构造了一种"机制",使"适配"的类可以很容

随机推荐