音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

PHP设计模式(五)适配器模式Adapter实例详解【结构型】
日期:2021-09-06 21:00:20   来源:脚本之家

本文实例讲述了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程序设计有所帮助。

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    ASP编程JSP编程PHP编程.NET编程python编程