在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器,用计算机访问照相机的 SD 内存卡时需要一个读卡器等。

在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。

模式的定义与特点

适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

该模式的主要优点如下。

客户端通过适配器可以透明地调用目标接口。

复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。

将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。

在很多业务场景中符合开闭原则。

其缺点是:

适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。

增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

模式的结构与实现

类适配器模式可采用多重继承方式实现,如 C++ 可定义一个适配器类来同时继承当前系统的业务接口和现有组件库中已经存在的组件接口;Java 不支持多继承,但可以定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。

对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。现在来介绍它们的基本结构。

1. 模式的结构

适配器模式(Adapter)包含以下主要角色。

目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。

适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。

适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

类适配器模式的结构图如图 1 所示。

图1 类适配器模式的结构图

对象适配器模式的结构图如图 2 所示。

图2 对象适配器模式的结构图

2. 模式的实现

(1) 类适配器模式的代码如下。

package adapter;

//目标接口

interface Target

{

public void request();

}

//适配者接口

class Adaptee

{

public void specificRequest()

{

System.out.println("适配者中的业务代码被调用!");

}

}

//类适配器类

class ClassAdapter extends Adaptee implements Target

{

public void request()

{

specificRequest();

}

}

//客户端代码

public class ClassAdapterTest

{

public static void main(String[] args)

{

System.out.println("类适配器模式测试:");

Target target = new ClassAdapter();

target.request();

}

}

程序的运行结果如下:

类适配器模式测试:

适配者中的业务代码被调用!

(2)对象适配器模式的代码如下。

package adapter;

//对象适配器类

class ObjectAdapter implements Target

{

private Adaptee adaptee;

public ObjectAdapter(Adaptee adaptee)

{

this.adaptee=adaptee;

}

public void request()

{

adaptee.specificRequest();

}

}

//客户端代码

public class ObjectAdapterTest

{

public static void main(String[] args)

{

System.out.println("对象适配器模式测试:");

Adaptee adaptee = new Adaptee();

Target target = new ObjectAdapter(adaptee);

target.request();

}

}

说明:对象适配器模式中的“目标接口”和“适配者类”的代码同类适配器模式一样,只要修改适配器类和客户端的代码即可。

程序的运行结果如下:

对象适配器模式测试:

适配者中的业务代码被调用!

模式的应用实例

【例1】用适配器模式(Adapter)模拟新能源汽车的发动机。

分析:新能源汽车的发动机有电能发动机(Electric Motor)和光能发动机(Optical Motor)等,各种发动机的驱动方法不同,例如,电能发动机的驱动方法 electricDrive() 是用电能驱动,而光能发动机的驱动方法 opticalDrive() 是用光能驱动,它们是适配器模式中被访问的适配者。

客户端希望用统一的发动机驱动方法 drive() 访问这两种发动机,所以必须定义一个统一的目标接口 Motor,然后再定义电能适配器(Electric Adapter)和光能适配器(Optical Adapter)去适配这两种发动机。

我们把客户端想访问的新能源发动机的适配器的名称放在 XML 配置文件中(点此下载 XML 文件),客户端可以通过对象生成器类 ReadXML 去读取。这样,客户端就可以通过 Motor 接口随便使用任意一种新能源发动机去驱动汽车,图 3 所示是其结构图。

图3 发动机适配器的结构图

程序代码如下:

package adapter;

//目标:发动机

interface Motor

{

public void drive();

}

//适配者1:电能发动机

class ElectricMotor

{

public void electricDrive()

{

System.out.println("电能发动机驱动汽车!");

}

}

//适配者2:光能发动机

class OpticalMotor

{

public void opticalDrive()

{

System.out.println("光能发动机驱动汽车!");

}

}

//电能适配器

class ElectricAdapter implements Motor

{

private ElectricMotor emotor;

public ElectricAdapter()

{

emotor=new ElectricMotor();

}

public void drive()

{

emotor.electricDrive();

}

}

//光能适配器

class OpticalAdapter implements Motor

{

private OpticalMotor omotor;

public OpticalAdapter()

{

omotor=new OpticalMotor();

}

public void drive()

{

omotor.opticalDrive();

}

}

//客户端代码

public class MotorAdapterTest

{

public static void main(String[] args)

{

System.out.println("适配器模式测试:");

Motor motor=(Motor)ReadXML.getObject();

motor.drive();

}

}

package adapter;

import javax.xml.parsers.*;

import org.w3c.dom.*;

import java.io.*;

class ReadXML

{

public static Object getObject()

{

try

{

DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();

DocumentBuilder builder=dFactory.newDocumentBuilder();

Document doc;

doc=builder.parse(new File("src/adapter/config.xml"));

NodeList nl=doc.getElementsByTagName("className");

Node classNode=nl.item(0).getFirstChild();

String cName="adapter."+classNode.getNodeValue();

Class> c=Class.forName(cName);

Object obj=c.newInstance();

return obj;

}

catch(Exception e)

{

e.printStackTrace();

return null;

}

}

}

程序的运行结果如下:

适配器模式测试:

电能发动机驱动汽车!

注意:如果将配置文件中的 ElectricAdapter 改为 OpticalAdapter,则运行结果如下:

适配器模式测试:

光能发动机驱动汽车!

模式的应用场景

适配器模式(Adapter)通常适用于以下场景。

以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。

使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。

模式的扩展

适配器模式(Adapter)可扩展为双向适配器模式,双向适配器类既可以把适配者接口转换成目标接口,也可以把目标接口转换成适配者接口,其结构图如图 4 所示。

图4 双向适配器模式的结构图

程序代码如下:

package adapter;

//目标接口

interface TwoWayTarget

{

public void request();

}

//适配者接口

interface TwoWayAdaptee

{

public void specificRequest();

}

//目标实现

class TargetRealize implements TwoWayTarget

{

public void request()

{

System.out.println("目标代码被调用!");

}

}

//适配者实现

class AdapteeRealize implements TwoWayAdaptee

{

public void specificRequest()

{

System.out.println("适配者代码被调用!");

}

}

//双向适配器

class TwoWayAdapter implements TwoWayTarget,TwoWayAdaptee

{

private TwoWayTarget target;

private TwoWayAdaptee adaptee;

public TwoWayAdapter(TwoWayTarget target)

{

this.target=target;

}

public TwoWayAdapter(TwoWayAdaptee adaptee)

{

this.adaptee=adaptee;

}

public void request()

{

adaptee.specificRequest();

}

public void specificRequest()

{

target.request();

}

}

//客户端代码

public class TwoWayAdapterTest

{

public static void main(String[] args)

{

System.out.println("目标通过双向适配器访问适配者:");

TwoWayAdaptee adaptee=new AdapteeRealize();

TwoWayTarget target=new TwoWayAdapter(adaptee);

target.request();

System.out.println("-------------------");

System.out.println("适配者通过双向适配器访问目标:");

target=new TargetRealize();

adaptee=new TwoWayAdapter(target);

adaptee.specificRequest();

}

}

程序的运行结果如下:

目标通过双向适配器访问适配者:

适配者代码被调用!

-------------------

适配者通过双向适配器访问目标:

目标代码被调用!

python适配器模式角色_适配器模式(Adapter模式)详解相关推荐

  1. 小例子背后的大道理——Adapter模式详解

    上回问题回顾 前文说到一位用户拿着业界标准开关(一个标准的StandardSwitcher,它依赖IStandardSwitchable接口才能工作,然而目前我们的灯并不支持这个接口)出现在我面前,叫 ...

  2. Python基本语法_输入/输出语句详解

    目录 目录 前言 输入 raw_input input raw_input 和 input 的区别 输出 print print 基本格式化输出 print复杂格式化输出 flags标志位 width ...

  3. PIC单片机入门_异步通讯模式详解

    1.USART用于异步通讯详解 1.1 USART的异步工作模式 在异步工作模式下, USART 采用的是标准非归零 ( NRZ编码格式 :一位起始位.8 位或 9 位数据位和一位停止位 ).最常用的 ...

  4. python normalize函数_归一化函数normalize详解

    opencv 2 归一化函数normalize详解 1. 归一化定义与作用 归一化就是要把需要处理的数据经过处理后 (通过某种算法)限制在你需要的一定范围内.首先归一化是为了后面数据处理的方便,其次是 ...

  5. getinstance方法详解_二、设计模式总览及工厂模式详解

    二.架构师内功心法之设计模式 2.架构师内功心法之设计模式 2.1.课程目标 1.通过对本章内容的学习,了解设计模式的由来. 2.介绍设计模式能帮我们解决哪些问题. 3.剖析工厂模式的历史由来及应用场 ...

  6. python对输入的字符串进行解析_python数据类型_字符串常用操作(详解)

    这次主要介绍字符串常用操作方法及例子 1.python字符串 在python中声明一个字符串,通常有三种方法:在它的两边加上单引号.双引号或者三引号,如下: name = 'hello' name1 ...

  7. 从java多态到策略模式_设计模式中的多态——策略模式详解

    2. 策略模式详解 2.1 策略模式定义 策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户端而独立的变化. 可以使用多态进行类比来理解策略模 ...

  8. python加密字符串小写字母循环后错两位_python数据类型_字符串常用操作(详解)

    这次主要介绍字符串常用操作方法及例子 1.python字符串 在python中声明一个字符串,通常有三种方法:在它的两边加上单引号.双引号或者三引号,如下: name = 'hello' name1 ...

  9. python贪婪匹配_python re模块匹配贪婪和非贪婪模式详解

    python re模块匹配贪婪和非贪婪模式详解 这篇文章主要介绍了python re模块匹配贪婪和非贪婪模式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友 ...

最新文章

  1. IDEA 显示类结构图
  2. 最强蜗牛击败毁灭机器人_最强蜗牛:恶魔形态解锁攻略
  3. windows 2008R2 无法安装操作系统补丁,或无法安装Sp1升级包的解决办法
  4. cisco 动态多点***原理
  5. 一个支持 CodeFirst/DbFirst/ModelFirst 的数据库小工具
  6. Spark SQL(四)之DataSet与RDD转换
  7. 基于携程游记的出行领域顺承事件图谱项目
  8. 清华北大的学子们都关注什么样公众号?
  9. 华为2018春招笔试题目 字节流解析与长整数相乘
  10. 高性能的 socket 通讯服务器(完成端口模型--IOCP)
  11. 《WebGL编程指南》学习笔记——1.WebGL概述
  12. linux redis集群工具,Redis集群部署及常用的操作命令
  13. STM32开发 | 移远4G-Cat.1模组EC200N-CN开发
  14. python爬虫之英汉互译(爬虫+pyqt5)
  15. mint-ui —— navbar的使用
  16. 【linux】查看环境变量|getenv setenv设置获取环境变量
  17. 【LOJ6570】毛毛虫计数
  18. iOS UITextView调整行间距
  19. Android studio编译错误
  20. 光照模型-兰伯特光照模型

热门文章

  1. 《中国人工智能学会通讯》——11.10 点云局部特征描述子基准评估体系
  2. 【数据结构笔记22】图的遍历例题:拯救007(应用DFS)、六度空间(应用BFS)
  3. 安装linux可是c盘文件夹失败,虚拟机安装linux系统,会对物理的磁盘有影响吗?怎样保证安全,谢了!...
  4. 【转】Linux的僵尸进程解决攻略
  5. JavaScript 中数组 sort() 方法的基本使用
  6. tnsname.ora 个参数解释
  7. java类注解是否可以通过实现接口或继承父类的方式获得
  8. javascript call 详细解答与实践
  9. 在eclipse中使用git创建本地库,以及托管项目到GitHub超详细教程
  10. 避免门事件 巧妙清除搜狗浏览器记录