适配器模式

简介: 主要内容有:该模式的介绍,包括:引子、意图(大白话解释)类图、时序图(理论规范)该模式的代码示例:熟悉该模式的代码长什么样子该模式的优缺点:模式不是万金油,不可以滥用模式该模式的实际使用案例:了解它在哪些重要的源码中被使用


前言



主要内容有:

  • 该模式的介绍,包括:
  • 引子、意图(大白话解释)
  • 类图、时序图(理论规范)
  • 该模式的代码示例:熟悉该模式的代码长什么样子
  • 该模式的优缺点:模式不是万金油,不可以滥用模式
  • 该模式的实际使用案例:了解它在哪些重要的源码中被使用


结构型——适配器模式



引子

当你从中国到国外旅游时,经常会购买一个电源适配器,也就是电源转接头,因为国外的插座经常无法适用于国内的插头,这个电源转接头就是一个适配器,也就是下图中的Adapter,它让你的插头能够正常的在国外使用。

客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。


定义

适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作

适配器模式有类的适配器模式和对象的适配器模式两种不同的形式。


1. 类适配器

创建新类,继承源类,同时还要实现新接口

class  adapter extends oldClass implements newFunc{}
复制代码


2. 对象适配器

创建新类的实例,其中包含旧的类,并实现新接口

class adapter implements newFunc { private oldClass oldInstance ;}
复制代码
  • 类适配器使用对象继承的方式,是静态的定义方式
  • 对象适配器使用对象组合的方式,是动态组合的方式。


类图

如果看不懂UML类图,可以先粗略浏览下该图,想深入了解的话,可以继续谷歌,深入学习:

适配器模式包含如下角色:

  • Target:目标抽象类:这就是所期待得到的新接口类。
  • Adapter:适配器类:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
  • Adaptee:适配者类:等待适配的旧类
  • Client:客户端调用者


1. 类适配器


2. 对象适配器


时序图

时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。

我们可以大致浏览下时序图,如果感兴趣的小伙伴可以去深究一下:


代码示例/使用场景举例



这次我将代码示例和使用场景两个章节合起来讲,是因为有一个十分典型的Java 源码可以学习


1. Java容器中的Enumeration老接口和Iterator新接口

JDK1.1 之前提供的容器有Arrays,Vector,Stack,Hashtable,Properties,BitSet,其中定义了一种访问群集内各元素的标准方式,称为 Enumeration(列举器)接口。

JDK1.2 版本中引入了 Iterator接口,新版本的集合对(HashSet,HashMap,WeakHeahMap,ArrayList,TreeSet,TreeMap, LinkedList)是通过 Iterator 接口访问集合元素。

这样,如果将老版本的程序运行在新的 Java 编译器上就会出错。因为 List 接口中已经没有 elements(),而只有 iterator() 了。那么如何将老版本的程序运行在新的 Java 编译器上呢? 如果不加修改,是肯定不行的,但是修改要遵循“开-闭”原则我们可以用 Java 设计模式中的适配器模式解决这个问题。

public class EnumIter implements Iterator {//重点1:配器实现目标接口
   private Enumeration e;//重点2:适配器持有被适配接口(对象)的引用
   public EnumIter(Enumeration e){
       this.e = e;
   }
   //将对目标对象的调用转发给真正的被适配的对象
   public boolean hasNext() {
       return e.hasMoreElements();
   }
   public Object next() {
       return e.nextElement();
   }
   public void remove() {
   }
}
复制代码

NewEnumeration 是一个适配器类,通过它实现了从 Iterator 接口到 Enumeration 接口的适配,这样我们就可以使用老版本的代码来使用新的集合对象了。


2. JDBC也是一种适配器模式

Sun公司在1996年公开了Java语言的数据库连接工具JDBC,JDBC使得Java语言程序能够与数据库连接,并使用SQL语言来查询和操作数据。JDBC给出一个客户端通用的抽象接口,每一个具体数据库引擎(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。抽象的JDBC接口和各个数据库引擎API之间都需要相应的适配器软件,这就是为各个不同数据库引擎准备的驱动程序。


模式优缺点



优点

  • 将目标类和适配者类解耦,而无须修改原有代码。
  • 对于客户端类来说是透明的,而且提高了适配者的复用性。

类适配器模式还具有如下优点:

由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

对象适配器模式还具有如下优点:

一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。


缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。

因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。


总结



建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。


参考



相关文章
|
iOS开发 MacOS Windows
Axure快速入门(03) - 丰富的元件库
Axure快速入门(03) - 丰富的元件库
1672 0
Axure快速入门(03) - 丰富的元件库
|
Linux 关系型数据库 Oracle
rhel7安装oracle数据库软件,compat-libstdc++-33缺失
Missing or Ignored package compat-libstdc++-33-3.2.3
6780 0
|
机器学习/深度学习 双11
基于机器学习的内存故障预测了解一下 | 双11备战
本文通过对服务器日志的分析,综合服务器的一些静态信息和状态信息,利用机器学习模型,进行服务器内存故障进行预测。
4458 0
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
301 1
|
12月前
|
存储 NoSQL 关系型数据库
【赵渝强老师】什么是NoSQL数据库?
随着大数据技术的兴起,NoSQL数据库(Not Only SQL)得到广泛应用。它不局限于二维表结构,允许数据冗余。常见的NoSQL数据库包括Redis、MongoDB和HBase。Redis是基于内存的高性能数据库,采用单线程模型和多路复用I/O,支持高效的数据结构。MongoDB使用BSON格式存储文档,查询语言强大,类似关系型数据库。HBase基于HDFS,适合数据分析,采用列式存储,支持灵活的列族设计。视频讲解及更多内容见下文。
594 79
|
Rust JavaScript Java
简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能
简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能
431 0
|
中间件 数据库 程序员
阿里云中间件是什么-阿里云中间件介绍
这其实是一个比较虚的概念。广义的中间件范围很广。起沟通作用的都可以认为是中间件。甚至ODBC这样的东西你也可以认为是中间件。   使用了中间件之后,以前直接连接的前台应用程序和数据库之前就多了个中间件,现在前台程序把请求发给中间件,中间件再把请求发给数据库,数据库处理结束之后把结果返回到中间件,中间件再把结果送回给前台。
4569 99
|
存储 安全 固态存储
阿里云aca考试通过率以及试题
不同于阿里云认证的其他考试,阿里云aca考试科目非常多,难度自然是比较高的,那么阿里云aca考试通过率怎么样呢?接下来为我们详细介绍阿里云aca考试通过率以及阿里云aca考试试题。
1451 0
阿里云aca考试通过率以及试题
|
XML 缓存 Java
通过源码理解Spring中@Scheduled的实现原理并且实现调度任务动态装载(上)
最近的新项目和数据同步相关,有定时调度的需求。之前一直有使用过Quartz、XXL-Job、Easy Scheduler等调度框架,后来越发觉得这些框架太重量级了,于是想到了Spring内置的Scheduling模块。而原生的Scheduling模块只是内存态的调度模块,不支持任务的持久化或者配置(配置任务通过@Scheduled注解进行硬编码,不能抽离到类之外),因此考虑理解Scheduling模块的底层原理,并且基于此造一个简单的轮子,使之支持调度任务配置:通过配置文件或者JDBC数据源。
685 1
通过源码理解Spring中@Scheduled的实现原理并且实现调度任务动态装载(上)
|
人工智能 自然语言处理 算法
天猫精灵开放平台体验
天猫精灵开放平台体验
1150 0
天猫精灵开放平台体验