设计模式(五)适配器模式Adapter(结构型)

2023-11-02

设计模式(五)适配器模式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 )在不改变它的接口的条件下,为另一个对象定义了一个代理。

装饰者模式,适配器模式,外观模式三者之间的区别:

装饰者模式的话,它并不会改变接口,而是将一个一个的接口进行装饰,也就是添加新的功能。

适配器模式是将一个接口通过适配来间接转换为另一个接口。

外观模式的话,其主要是提供一个整洁的一致的接口给客户端。



转载于:https://www.cnblogs.com/iplus/archive/2012/05/02/4490232.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式(五)适配器模式Adapter(结构型) 的相关文章

  • 将 OAuth WRAP 访问令牌直接保存在客户端计算机上的 cookie 中吗?

    我计划建立一个可以访问 oauth 包装框架的网站 我正在考虑将访问令牌按原样存储在客户端计算机上 我不想在服务器上维护临时令牌等数据库 我应该做吗 或者我应该加密它 首先 为什么他们不使用 OAuth 2 0 您可以将 OAuth 凭据存
  • 收到警告“标头不能包含多个标头,检测到新行”

    我正在用 oops 进行编码 以便用 PHP 上传图像 但是提交图片后却出现警告 标题不能包含多个标题 检测到新行 下面是我的函数 它给出了错误 public function ft redirect query if REQUEST UR
  • 检查字符串是否是哈希值

    我正在使用 SHA 512 来散列我的密码 当然还有盐 我认为我想要的不可能 但无论如何我们还是要问一下 有没有办法检查字符串是否已经是 SHA 512 或其他算法 哈希值 当用户登录时 我想检查他的密码 如果它仍然是纯文本 则应将其转换为
  • 纯基于网络的版本控制系统

    我的托管服务当前不允许在其服务器上运行 允许 svn git cvs 我真的希望能够将我的开发计算机上的当前源代码与我的生产服务器 同步 我正在寻找一个纯php python ruby版本控制系统 不只是一个client对于版本控制系统 不
  • 从 php 到 JavaScript 的数组

    我正在尝试使用 json 将数组列表从 php 传输到 javascript 但它不起作用 JS ajax url getProfilePhotos php type post post or get method data if you
  • 显示和随机化 php 数组

    我有一个显示结果的数组 如下所示 Array 0 gt 71 1 gt 56 2 gt 64 3 gt 82 4 gt 90 5 gt 80 6 gt 65 7 gt 62 8 gt 14 9 gt 3 我的代码是 while row my
  • Laravel/00webhost 错误 404。在此服务器上找不到请求的 URL

    1 将我的文件上传到 000webhost 我将公用文件夹中的所有文件放置到公共 html然后我创建了一个名为laravel我在那里上传了所有其他文件 这是我的目录结构 laravel app 引导程序 config 公共 html 索引
  • PHP-docker容器中的环境变量

    我想在我的 docker 容器中显示一个环境变量 PHP 脚本如下所示 我使用 OpenShift 来启动容器 PHP 容器显示 env is 现在我更改容器的 dc 配置 oc env dc envar USER Pieter deplo
  • 如何使用 jQuery Ajax 将 PHP 数组值传递到另一个文件?

    这是我的代码
  • 如何在同一 PHP 页面上多次使用 mysqli fetch_assoc() 和准备好的语句?

    有没有办法启用fetch assoc 在同一页上多次使用准备好的语句 data conn gt prepare SELECT FROM some table WHERE id data gt bind param i id data gt
  • PHP 脚本不断执行 mmap/munmap

    我的 PHP 脚本包含一个循环 它只不过是回显和取消引用指针 如 tab othertab i gt 中的内容 直到昨天 这个脚本开始变得非常慢 比以前慢了 50 倍 之前 它一直运行良好 使用 strace 后 我发现 90 的情况下 脚
  • 合并 2 个数组并合并数字键的结果

    我有 2 个数组 我希望通过每个数字键将其中合并 分组在一起 例如 Array1 2009 gt 131 2008 gt 940 2007 gt 176 2006 gt 1 Array2 2008 gt 9 2007 gt 3 我希望输出是
  • 使用 yum 和 pear 安装 php-soap 均失败

    我正在尝试在 Centos 6 4 服务器上安装 PHP 的 SOAP 扩展 我对包管理器 从 CLI 安装包并在 PHP 中配置它们相当不熟悉 我相当有能力管理 php ini 和其他 PHP 配置文件 soap ini 等 我尝试使用以
  • PHP Intl 扩展线程安全吗?

    我一直在阅读有关 PHP 中的语言环境的内容 看起来setlocale 线程有问题 我对线程不太熟悉 文档提到它不是线程安全的 我想让我的项目能够处理某些数字格式 并且 Intl 扩展似乎很有趣 http php net manual en
  • ACL授权失败后ZF3重定向

    我有一个带有 ACL 的新 ZF3 应用程序 现在 我需要在未经授权的访问的情况下重定向到错误页面 例如 403 我认为最好的方法是触发一个事件 然后捕获它 但我失败了 全部都在我的用户模块中Module php 摘录 namespace
  • 设置大型电子邮件通知系统有哪些方法?

    我的公司有一个用 PHP 构建的网站 我们使用内置的 PHP 电子邮件功能每天向订阅者发送数千封电子邮件 这是一个糟糕的主意 它堵塞了我们的服务器 并且需要几个小时才能完成整个批次 现在我已经研究过像 MailChimp 这样的群发邮件服务
  • 使用 ImageMagick (PHP) 将 2 个图像并排合并为 1 个图像

    我认为这是一件容易的事 我有 2 张图片 JPG 我希望它们合并成一张图片 其中 2 张图片并排 所以我有图片 A 和图片 B 我想要图片 AB 并排 两个图像具有相同的宽度和高度 在本例中 宽度 200px 高度 300px 但是第二个图
  • PHP条件,如果当前页面,则链接突出显示[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我有一个带
  • 如何将变量插入 PHP 数组?

    我在网上查了一些答案 但都不是很准确 我希望能够做到这一点 id result id info array id Example echo info 0 这有可能吗 您需要的是 不推荐 info array id Example varia
  • 我可以让 swagger-php 在查询字符串上使用数组吗?

    我使用 Swagger php 当我定义查询字符串上的参数时 它可以是一个数组 但据我所知 它不支持这种查询字符串 https api domain tld v1 objects q 1 q 5 q 12 我相信这会被设定in the co

随机推荐

  • 深度理解取整&取余&取模运算

    在编程的学习当中 我们会经常行的使用这些操作在表达式计算 但是你在使用当中 你真的理解了吗 或者说是你完全学会使用了 在这篇博客当中 或许会出现错误 希望大家理解 目前还在学习当中 发现错误或不足之处请大家斧正 目录 一 取整 二 取余与取
  • 64位win7下安装MongoDB以zip包的形式 图文(超详细)

    首先从mongodb的官网上下载对应版本的zip包 如果你使用Windows 64 bit 2008 R2 或win7需要安装Hotfix补丁 读者可以去网上下载相应的版本 解压后会得到如下的一个目录 然后自己在某个录下下建好一个目录 我这
  • window.open (‘page.html‘)

    window open page html 用于控制弹出新的窗口
  • 2024王道408数据结构 P143 T8

    2024王道408数据结构 P143 T8 思考过程 首先题目的意思非常简单明了 就是让我们找二叉树中度为2的结点 也就是既有左子树又有右子树的结点 那我们只需要在代码里判断如果该结点有左子树就入队 同时如果该结点有右子树就计数器 1 并且
  • vi/vim基本使用命令

    转自 http www lupaworld com uid 296380 action viewspace itemid 118973 vi vim 基本使用方法 本文介绍了vi vim 的基本使用方法 但对于普通用户来说基本上够了 i v
  • QT+VS配置及调试

    QT下载 https download qt io archive qt QT Creator设置 打开 Qt Creator 进入编译器部分 工具 gt 选项 gt 构建和运行 gt 编译器 可以看到vs的内容 之后 进入 工具 gt 选
  • Android平台GB28181接入端如何对接UVC摄像头?

    我们在对接Android平台GB28181接入的时候 有公司提出这样的需求 除了采集执法记录仪摄像头自带的数据外 还想通过执法记录仪采集外接UVC摄像头 实际上 这块对我们来说有点炒冷饭了 不算新的诉求 大牛直播SDK 在2016年对接RT
  • 2023国庆节放假通知

    喜迎国庆 放假通知 公司相关各部门 国庆来临之际 根据国家有关规定 现将2023年国庆放假事项通知如下 1 9月29至10月6日放假调休 共8天 10月7日上班 10月8日上班 2 各部门接通知后 妥善安排好值班工作 并将各部门值班表于20
  • 500套优秀简历模板,送给您!

    点击上方 成猿之路 选择 置顶公众号 第一时间送达实用技术干货 最近收藏保存了一些简历模板 觉得不错 送给即将步入社会或需要简历模板的你 01单面简历 150款 02多页简历 95款 03表格简历 18款 04英文简历 27款 05艺术气质
  • pytorch: 数据增广(Data Augmentation)

    常用的数据增广方法 比例缩放 位置截取 翻转 旋转 亮度 对比度和色调的变化 读取原图 import torchvision transforms as transforms from PIL import Image img Image
  • Vue.js如何实现倒计时?颜小白实测可用!

    Vue JS如何实现倒计时功能 1 首先一般来说前端小伙伴们会收到后端同学传过来的一个结束时间 大部分需要倒计时得情况都是在详情页 比如商品 活动等一些场景 2 如果需要实现倒计时功能 首先我们需要知道如何计算剩余时间 首先我们会拿到后端传
  • jdbctemplate 执行多条sql_SpringBoot使用JdbcTemplate连接Mysql实现增删改查

    摘要 本文是springboot工程使用JdbcTemplate连接Mysql数据库 实现增删改查的实例 及在搭建过程中碰到的几个问题 前几篇介绍怎么搭建SpringBoot工程 接下来直接入正题 什么是JDBC JDBC Java Dat
  • HDFS RPC限流方案实践探索

    文章目录 前言 HDFS RPC限流方案 分级RPC queue的调参 分级RPC queue的insight 前言 在前面的一篇关于分布式集群下的限流方案文章里 笔者阐述了一种在HDFS集群里的RPC限流架构 其间也提到了很多关于分布式限
  • Ubuntu连接不上网络问题的解决方法

    这学期经常要用虚拟机做实验 但经常在某一次开机后网络连接不上 查过很多解决方法 每次奏效的方法又都不一样 这里记录一下 省的下次一个一个找了 第一次写博客 有点点小激动嘿嘿 以下方法都是在NAT模式下的连接 方法一 还原默认设置 将虚拟机关
  • 20个基本电路图讲解_记住这些规则,再看电路图就不会乱了!

    在我们进行电子DIY制作时 看图是难免的 但对于很多新手来说 刚开始似乎总有种很乱的感觉 走过来后我们才知道 当时只是没有了解这些规则 今天小编以电子电路图为主要示例进行总结一下 电路图走向 是指电路图中各部分电路 从最初的输入端到最终的输
  • 【数据结构】 二叉树面试题讲解->叁

    文章目录 引言 根据二叉树创建字符串 https leetcode cn problems construct string from binary tree submissions 题目描述 示例 示例一 示例二 思路解析 代码完整实现
  • Spring MVC的表单标签库详解

    表单标签库中包含了可以用在 JSP 页面中渲染 HTML 元素的标签 在 JSP 页面使用 Spring 表单标签库时 必须在 JSP 页面开头处声明 taglib 指令 指令代码如下 在表单标签库中有 form input passwor
  • Ueditor去掉图片之间的间隙

    问题 运营在后台配置商品信息的时候 复制京东上面的图片到ueditor富文本编辑器里面 两张图片中总是存在空白间隙 但查看html源码又很简单没有发现什么问题p标签之类的 而且硬着配置上去后 在uniapp打包的微信小程序里面查看商品详情一
  • cartographer 前端PoseExtrapolator及IMU预积分

    卡尔曼滤波器应用 用于单目标追踪的IMM模型 知乎 Cartographer前端的优化 基于IMU预积分的LIO实现 知乎 Cartographer PoseExtrapolator 位姿外推器 分析总结与优化思路 知乎 MCGA Make
  • 设计模式(五)适配器模式Adapter(结构型)

    设计模式 五 适配器模式Adapter 结构型 1 概述 接口的改变 是一个需要程序员们必须 虽然很不情愿 接受和处理的普遍问题 程序提供者们修改他们的代码 系统库被修正 各种程序语言以及相关库的发展和进化 例子1 iphone4 你即可以