当前位置: 首页 > news >正文

重读Java设计模式: 适配器模式解析

引言

在软件开发中,经常会遇到不同接口之间的兼容性问题。当需要使用一个已有的类,但其接口与我们所需的不兼容时,我们可以通过适配器模式来解决这一问题。适配器模式是一种结构型设计模式,它允许接口不兼容的类之间进行合作。本文将深入探讨适配器模式的概念、应用场景以及在Java中的实现方式。

一、理解适配器模式

1.1 什么是适配器模式?

适配器模式是一种结构型设计模式,旨在将一个类的接口转换为另一个类的接口,以使原本不兼容的类能够一起工作。适配器模式通常涉及一个适配器类,该类充当两个不兼容接口之间的桥梁,使得它们可以相互协作。

1.2 适配器模式的角色

在适配器模式中,通常有以下几个角色:

  • 目标接口(Target):定义客户端使用的特定领域接口。
  • 适配器(Adapter):实现目标接口,并包装一个或多个不兼容的类,以使其与客户端一起工作。
  • 被适配者(Adaptee):拥有需要被适配的接口,但与目标接口不兼容。

二、适配器模式的应用场景

2.1 与现有代码的集成

当我们需要在现有代码基础上添加新的功能或组件时,通常会遇到新旧代码之间接口不兼容的情况。此时,适配器模式可以帮助我们将新组件与现有代码进行无缝集成,而无需修改已有的代码。

2.2 复用现有功能

有时我们可能会有一些功能强大但接口不兼容的类库,而我们希望利用这些功能来实现自己的需求。适配器模式可以将这些现有类库包装在一个适配器中,以便我们可以轻松地在自己的项目中复用这些功能。

三、Java 中的适配器模式实现

在Java中,适配器模式常见的实现方式包括类适配器和对象适配器两种。

3.1 类适配器

类适配器通过继承被适配者类并实现目标接口来实现适配器。下面是一个简单的示例:

// 目标接口
interface Target {void request();
}// 被适配者
class Adaptee {void specificRequest() {System.out.println("Adaptee's specific request");}
}// 类适配器
class Adapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();}
}

3.2 对象适配器

对象适配器通过将被适配者对象作为适配器的一个成员变量来实现适配器。下面是一个简单的示例:

// 对象适配器
class Adapter implements Target {private Adaptee adaptee;Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}

3.3 基于对象适配器实现一个真实的案例

拿我身边的事物举例:我有一个 mac 笔记本电脑,现在我想拓展一个显示器,但是我买的显示器提供的接口仅支持 HDMI 接口,而我的电脑上并不支持这个接口,怎么办呢?这时候拓展坞就出现了,它将显示器和笔记本电脑连接在了一起,实现了显示器拓展屏的功能。场景如下图所示:

在这里插入图片描述

这就是一个典型的适配器模式场景,我们来看下职责划分:

  • type-c 接口就是目标接口
  • 拓展坞 就是适配器
  • HDMI 接口就是被适配器

总体就是 HDMI 接口通过拓展坞适配成了 type-c 接口插入电脑使用。我们来看下代码实现:

  • 拓展坞及其支持的插槽
package com.markus.desgin.mode.structural.adapter;import static com.markus.desgin.mode.structural.adapter.ComputerSlot.HDMI;
import static com.markus.desgin.mode.structural.adapter.ComputerSlot.USB;public class ComputerAdapter implements AdvancedComputerSlot {ComputerSlot usb = new USBSlot();ComputerSlot hdmi = new HDMISlot();public ComputerAdapter() {}@Overridepublic void typeC(String type) {switch (type) {case HDMI:hdmi.hdmi();break;case USB:usb.usb();break;default:throw new UnsupportedOperationException("拓展坞中没有该类型的数据插槽!");}}
}public interface ComputerSlot {String HDMI = "HDMI";String USB = "USB";void hdmi();void usb();String type();
}public class HDMISlot implements ComputerSlot {@Overridepublic void hdmi() {System.out.println("数据线插入 HDMI 接口成功!");}@Overridepublic void usb() {throw new UnsupportedOperationException("该数据线不允许插入当前插槽");}@Overridepublic String type() {return HDMI;}
}public class USBSlot implements ComputerSlot {@Overridepublic void hdmi() {throw new UnsupportedOperationException("该数据线不允许插入当前插槽");}@Overridepublic void usb() {System.out.println("数据线插入 USB 接口成功!");}@Overridepublic String type() {return USB;}
}
  • 目标接口
public interface AdvancedComputerSlot {String TYPEC = "TYPE-C";void typeC(String type);
}public class AdvancedComputerSlotImpl implements AdvancedComputerSlot {ComputerAdapter adapter = new ComputerAdapter();@Overridepublic void typeC(String type) {switch (type) {case ComputerSlot.HDMI:case ComputerSlot.USB:adapter.typeC(type);break;case TYPEC:System.out.println("Type-C 接口插入成功!");break;default:throw new UnsupportedOperationException("暂时不支持插入其他类型");}}
}
  • 客户端
public class AdapterPatternDemo {public static void main(String[] args) {AdvancedComputerSlot advancedComputerSlot = new AdvancedComputerSlotImpl();advancedComputerSlot.typeC(TYPEC);advancedComputerSlot.typeC(HDMI);advancedComputerSlot.typeC(USB);}
}

四、适配器模式在 Spring 框架中的使用

4.1 org.springframework.web.servlet.HandlerAdapter

在 Spring MVC 中,HandlerAdapter 负责将请求分发给相应的处理器(Handler)。不同类型的处理器可能具有不同的接口,而 HandlerAdapter 则负责将不同类型的处理器适配为统一的处理器接口,从而实现请求的统一处理。

我们来看下 HandlerAdapter 的接口设计以及部分实现类:

public interface HandlerAdapter {/*** Given a handler instance, return whether this {@code HandlerAdapter}* can support it. Typical HandlerAdapters will base the decision on the handler* type. HandlerAdapters will usually only support one handler type each.* <p>A typical implementation:* <p>{@code* return (handler instanceof MyHandler);* }* @param handler the handler object to check* @return whether this object can use the given handler*/boolean supports(Object handler);/*** Use the given handler to handle this request.* The workflow that is required may vary widely.* @param request current HTTP request* @param response current HTTP response* @param handler the handler to use. This object must have previously been passed* to the {@code supports} method of this interface, which must have* returned {@code true}.* @return a ModelAndView object with the name of the view and the required* model data, or {@code null} if the request has been handled directly* @throws Exception in case of errors*/@NullableModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;/*** Same contract as for HttpServlet's {@code getLastModified} method.* Can simply return -1 if there's no support in the handler class.* @param request current HTTP request* @param handler the handler to use* @return the lastModified value for the given handler* @deprecated as of 5.3.9 along with* {@link org.springframework.web.servlet.mvc.LastModified}.*/@Deprecatedlong getLastModified(HttpServletRequest request, Object handler);}
public class HttpRequestHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return (handler instanceof HttpRequestHandler);}@Override@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {((HttpRequestHandler) handler).handleRequest(request, response);return null;}@Override@SuppressWarnings("deprecation")public long getLastModified(HttpServletRequest request, Object handler) {if (handler instanceof LastModified) {return ((LastModified) handler).getLastModified(request);}return -1L;}}

4.2 org.springframework.aop.framework.adapter.AdvisorAdapter

它是 Spring AOP 框架中的一个重要组件,将不同类型的 Advisor 适配成统一的 MethodInterceptor(Advice)的工具。在 Spring AOP 中,Advisor 是将通知应用到切点上的对象,而 MethodInterceptor 是实际执行通知逻辑的对象。

Spring AOP 将不同类型的通知(Before、After、Around、Throws 等)转换为相应的 Advisor,并将其适配到切点上。在运行时,每个 Advisor 都被转换为一个 MethodInterceptor,并应用于目标方法的执行。

我们也来看下它的相关接口定义和部分实现:

public interface AdvisorAdapter {/*** Does this adapter understand this advice object? Is it valid to* invoke the {@code getInterceptors} method with an Advisor that* contains this advice as an argument?* @param advice an Advice such as a BeforeAdvice* @return whether this adapter understands the given advice object* @see #getInterceptor(org.springframework.aop.Advisor)* @see org.springframework.aop.BeforeAdvice*/boolean supportsAdvice(Advice advice);/*** Return an AOP Alliance MethodInterceptor exposing the behavior of* the given advice to an interception-based AOP framework.* <p>Don't worry about any Pointcut contained in the Advisor;* the AOP framework will take care of checking the pointcut.* @param advisor the Advisor. The supportsAdvice() method must have* returned true on this object* @return an AOP Alliance interceptor for this Advisor. There's* no need to cache instances for efficiency, as the AOP framework* caches advice chains.*/MethodInterceptor getInterceptor(Advisor advisor);}
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof MethodBeforeAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();return new MethodBeforeAdviceInterceptor(advice);}}

五、设计模式百宝箱

  • 在本节,我们开始填充我们的百宝箱:

    • 面向对象基础
      • 抽象
      • 封装
      • 多态
      • 继承
    • 面向对象原则
      • 依赖抽象,不要依赖具体类
      • 针对接口编程,不针对具体实现编程
      • 类应该对扩展开放,对修改关闭
      • 为交互对象之间的松耦合设计而努力
      • 多用组合,少用继承(尽管有类适配器的实现方式,多数情况下我们都是使用的对象适配器)
    • 面向对象设计模式
      • 简单工厂模式:定义了一个创建对象的接口,将创建对象的内容从客户端抽离出来
      • 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
      • 原型模式:通过复制现有对象来创建新对象,提高代码效率和可维护性
      • 建造者模式:将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示
      • 适配器模式:将一个类的接口转换成客户期望的另一个接口。适配器让原来接口不兼容的类可以合作无间

六、总结

本文深入探讨了适配器模式的概念、应用场景以及在 Java 中的实现方式。首先介绍了适配器模式的基本概念,包括目标接口、适配器和被适配者等角色。然后,通过示例演示了类适配器和对象适配器两种实现方式,并以一个真实场景的例子说明了适配器模式的具体应用。

在介绍了适配器模式的基本概念和实现方式后,文章进一步探讨了适配器模式在 Spring 框架中的应用。通过分析 org.springframework.web.servlet.HandlerAdapterorg.springframework.aop.framework.adapter.AdvisorAdapter 这两个类的设计和实现,展示了适配器模式在 Spring 框架中的重要作用。

适配器模式是一种非常常用且灵活的设计模式,在实际开发中经常能够见到其身影。通过本文的介绍,读者可以更深入地理解适配器模式的作用及其在实际项目中的应用,为日后的软件设计和开发提供参考和借鉴。

相关文章:

重读Java设计模式: 适配器模式解析

引言 在软件开发中&#xff0c;经常会遇到不同接口之间的兼容性问题。当需要使用一个已有的类&#xff0c;但其接口与我们所需的不兼容时&#xff0c;我们可以通过适配器模式来解决这一问题。适配器模式是一种结构型设计模式&#xff0c;它允许接口不兼容的类之间进行合作。本…...

MySQL面试题系列-9

MySQL是一个关系型数据库管理系统&#xff0c;由瑞典 MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。MySQL是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的RDBMS (Relational Database Management System&#xff0c;关系数据…...

书生·浦语训练营二期第二次笔记

文章目录 1. 部署 InternLM2-Chat-1.8B 模型进行智能对话1.1 配置环境1.2 下载 InternLM2-Chat-1.8B 模型 2. 实战&#xff1a;部署实战营优秀作品 八戒-Chat-1.8B 模型2.1 配置基础环境2.2 使用 git 命令来获得仓库内的 Demo 文件&#xff1a;2.3 下载运行 Chat-八戒 Demo 3. …...

python_3

文章目录 题目运行结果模式A模式B模式C模式D 题目 mode input("请选择模式:") n int(input("请输入数字:"))if mode "A" or mode "a":# 模式A n:输入的层数 i:当前的层数# 每行数字循环次数 ifor i in range(1, n 1):for j in r…...

【Python】 使用Apache Tika和Python实现zip、csv、xls等多格式文件文本内容提取

时间的电影 结局才知道 原来大人已没有童谣 最后的叮咛 最后的拥抱 我们红着眼笑 我们都要把自己照顾好 好到遗憾无法打扰 好好的生活 好好的变老 好好假装我 已经把你忘掉 &#x1f3b5; 五月天《好好》 在进行数据分析、搜索引擎优化或任何需要处理大量…...

C语言如何将多维数组名作为函数参数?

一、问题 ⼦函数执⾏时&#xff0c;整个多维数组是由主函数决定的&#xff0c;这时就要把多维数组的数组名作为函数参数传递给⼦函数。那么在C程序中&#xff0c;怎样将多维数组名作函数参数进⾏传递&#xff1f; 二、解答 以⼆维数组为例&#xff0c;其格式如下。 形参定义&…...

2013年认证杯SPSSPRO杯数学建模C题(第二阶段)公路运输业对于国内生产总值的影响分析全过程文档及程序

2013年认证杯SPSSPRO杯数学建模 C题 公路运输业对于国内生产总值的影响分析 原题再现&#xff1a; 交通运输作为国民经济的载体&#xff0c;沟通生产和消费&#xff0c;在经济发展中扮演着极其重要的角色。纵观几百年来交通运输与经济发展的相互关系&#xff0c;生产水平越高…...

《LeetCode力扣练习》代码随想录——二叉树(合并二叉树---Java)

《LeetCode力扣练习》代码随想录——二叉树&#xff08;合并二叉树—Java&#xff09; 刷题思路来源于 代码随想录 617. 合并二叉树 二叉树-前序遍历 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode…...

openstack云计算(二)——使用Packstack安装器安装一体化OpenStack云平台

初步掌握OpenStack快捷安装的方法。掌握OpenStack图形界面的基本操作。 一【准备阶段】 &#xff08;1&#xff09;准备一台能够安装OpenStack的实验用计算机&#xff0c;建议使用VMware虚拟机。 &#xff08;2&#xff09;该计算机应安装CentOS 7&#xff0c;建议采用CentO…...

Flutter Don‘t use ‘BuildContext‘s across async gaps.

Flutter提示Don‘t use ‘BuildContext‘s across async gaps.的解决办法—flutter里state的mounted属性...

基于SSM+Jsp+Mysql的个性化影片推荐系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…...

循环队列的实现及应用——桶排序bucket_sort、基数排序radix_sort

一、循环队列的实现 代码解释 1、完成初始化 2、定义方法 3、测试实例 4、完整代码 class AQueue:def __init__(self, size=10):self.__mSize = sizeself.__front=0self.__rear = 0self.__listArray = [None] * size#清空元素def clear(self):self.__front = 0self.__rear =…...

ubuntu16如何使用高版本cmake

1.引言 最近在尝试ubuntu16.04下编译开源项目vsome&#xff0c;发现使用apt命令默认安装cmake的的版本太低。如下 最终得知&#xff0c;ubuntu16默认安装确实只能到3.5.1。解决办法只能是源码安装更高版本。 2.源码下载3.20 //定位到opt目录 cd /opt 下载 wget https://cmak…...

电商-广告投放效果分析(KMeans聚类、数据分析-pyhton数据分析

电商-广告投放效果分析&#xff08;KMeans聚类、数据分析&#xff09; 文章目录 电商-广告投放效果分析&#xff08;KMeans聚类、数据分析&#xff09;项目介绍数据数据维度概况数据13个维度介绍 导入库&#xff0c;加载数据数据审查相关性分析数据处理建立模型聚类结果特征分析…...

练习 16 Web [极客大挑战 2019]LoveSQL

extractvalue(1,concat(‘~’, (‘your sql’) ) )报错注入&#xff0c;注意爆破字段的时候表名有可能是table_name不是table_schema 有登录输入框 常规尝试一下 常规的万能密码&#xff0c;返回了一个“admin的密码”&#xff1a; Hello admin&#xff01; Your password is…...

C++——栈和队列容器

前言&#xff1a;这篇文章我们将栈和队列两个容器放在一起进行分享&#xff0c;因为这两个要分享的知识较少&#xff0c;而且两者在结构上有很多相似之处&#xff0c;比如栈只能在栈顶操作&#xff0c;队列只能在队头和队尾操作。 不同于前边所分享的三种容器&#xff0c;这篇…...

Java集合(个人整理笔记)

目录 1. 常见的集合有哪些&#xff1f; 2. 线程安全的集合有哪些&#xff1f;线程不安全的呢&#xff1f; 3. Arraylist与 LinkedList 异同点&#xff1f; 4. ArrayList 与 Vector 区别&#xff1f; 5. Array 和 ArrayList 有什么区别&#xff1f;什么时候该应 Array而不是…...

Redis -- 缓存穿透问题解决思路

缓存穿透 &#xff1a;缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 常见的解决方案有两种&#xff1a; 缓存空对象 优点&#xff1a;实现简单&#xff0c;维护方便 缺点&#xff1a; 额外…...

数据挖掘中的PCA和KMeans:Airbnb房源案例研究

目录 一、PCA简介 二、数据集概览 三、数据预处理步骤 四、PCA申请 五、KMeans 聚类 六、PCA成分分析 七、逆变换 八、质心分析 九、结论 十、深入探究 10.1 第 1 步&#xff1a;确定 PCA 组件的最佳数量 10.2 第 2 步&#xff1a;使用 9 个组件重做 PCA 10.3 解释 PCA 加载和特…...

【ArcGIS微课1000例】0107:ArcGIS加载在线历史影像服务WMTS

文章目录 一、WMTS历史影像介绍二、ArcGIS加载WMTS服务三、Globalmapper加载WMTS服务一、WMTS历史影像介绍 通过访问历史影响WMTS服务,可以将全球范围内历史影像加载进来,如下所示: WMTS服务: https://wayback.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/WM…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

es6+和css3新增的特性有哪些

一&#xff1a;ECMAScript 新特性&#xff08;ES6&#xff09; ES6 (2015) - 革命性更新 1&#xff0c;记住的方法&#xff0c;从一个方法里面用到了哪些技术 1&#xff0c;let /const块级作用域声明2&#xff0c;**默认参数**&#xff1a;函数参数可以设置默认值。3&#x…...

土建施工员考试:建筑施工技术重点知识有哪些?

《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目&#xff0c;核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容&#xff0c;附学习方向和应试技巧&#xff1a; 一、施工组织与进度管理 核心目标&#xff1a; 规…...

密码学基础——SM4算法

博客主页&#xff1a;christine-rr-CSDN博客 ​​​​专栏主页&#xff1a;密码学 &#x1f4cc; 【今日更新】&#x1f4cc; 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 ​编辑…...

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”&#xff0c;于是尝试整合图像识别与网页自动化技术&#xff0c;完成了这套模拟登录流程。核心思路是&#xff1a;截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...