重读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.HandlerAdapter
和 org.springframework.aop.framework.adapter.AdvisorAdapter
这两个类的设计和实现,展示了适配器模式在 Spring 框架中的重要作用。
适配器模式是一种非常常用且灵活的设计模式,在实际开发中经常能够见到其身影。通过本文的介绍,读者可以更深入地理解适配器模式的作用及其在实际项目中的应用,为日后的软件设计和开发提供参考和借鉴。
相关文章:
重读Java设计模式: 适配器模式解析
引言 在软件开发中,经常会遇到不同接口之间的兼容性问题。当需要使用一个已有的类,但其接口与我们所需的不兼容时,我们可以通过适配器模式来解决这一问题。适配器模式是一种结构型设计模式,它允许接口不兼容的类之间进行合作。本…...
MySQL面试题系列-9
MySQL是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的RDBMS (Relational Database Management System,关系数据…...
书生·浦语训练营二期第二次笔记
文章目录 1. 部署 InternLM2-Chat-1.8B 模型进行智能对话1.1 配置环境1.2 下载 InternLM2-Chat-1.8B 模型 2. 实战:部署实战营优秀作品 八戒-Chat-1.8B 模型2.1 配置基础环境2.2 使用 git 命令来获得仓库内的 Demo 文件: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等多格式文件文本内容提取
时间的电影 结局才知道 原来大人已没有童谣 最后的叮咛 最后的拥抱 我们红着眼笑 我们都要把自己照顾好 好到遗憾无法打扰 好好的生活 好好的变老 好好假装我 已经把你忘掉 🎵 五月天《好好》 在进行数据分析、搜索引擎优化或任何需要处理大量…...
C语言如何将多维数组名作为函数参数?
一、问题 ⼦函数执⾏时,整个多维数组是由主函数决定的,这时就要把多维数组的数组名作为函数参数传递给⼦函数。那么在C程序中,怎样将多维数组名作函数参数进⾏传递? 二、解答 以⼆维数组为例,其格式如下。 形参定义&…...
2013年认证杯SPSSPRO杯数学建模C题(第二阶段)公路运输业对于国内生产总值的影响分析全过程文档及程序
2013年认证杯SPSSPRO杯数学建模 C题 公路运输业对于国内生产总值的影响分析 原题再现: 交通运输作为国民经济的载体,沟通生产和消费,在经济发展中扮演着极其重要的角色。纵观几百年来交通运输与经济发展的相互关系,生产水平越高…...
《LeetCode力扣练习》代码随想录——二叉树(合并二叉树---Java)
《LeetCode力扣练习》代码随想录——二叉树(合并二叉树—Java) 刷题思路来源于 代码随想录 617. 合并二叉树 二叉树-前序遍历 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode…...
openstack云计算(二)——使用Packstack安装器安装一体化OpenStack云平台
初步掌握OpenStack快捷安装的方法。掌握OpenStack图形界面的基本操作。 一【准备阶段】 (1)准备一台能够安装OpenStack的实验用计算机,建议使用VMware虚拟机。 (2)该计算机应安装CentOS 7,建议采用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的个性化影片推荐系统
开发语言:Java框架:ssm技术:JSPJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件: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,发现使用apt命令默认安装cmake的的版本太低。如下 最终得知,ubuntu16默认安装确实只能到3.5.1。解决办法只能是源码安装更高版本。 2.源码下载3.20 //定位到opt目录 cd /opt 下载 wget https://cmak…...
电商-广告投放效果分析(KMeans聚类、数据分析-pyhton数据分析
电商-广告投放效果分析(KMeans聚类、数据分析) 文章目录 电商-广告投放效果分析(KMeans聚类、数据分析)项目介绍数据数据维度概况数据13个维度介绍 导入库,加载数据数据审查相关性分析数据处理建立模型聚类结果特征分析…...
练习 16 Web [极客大挑战 2019]LoveSQL
extractvalue(1,concat(‘~’, (‘your sql’) ) )报错注入,注意爆破字段的时候表名有可能是table_name不是table_schema 有登录输入框 常规尝试一下 常规的万能密码,返回了一个“admin的密码”: Hello admin! Your password is…...
C++——栈和队列容器
前言:这篇文章我们将栈和队列两个容器放在一起进行分享,因为这两个要分享的知识较少,而且两者在结构上有很多相似之处,比如栈只能在栈顶操作,队列只能在队头和队尾操作。 不同于前边所分享的三种容器,这篇…...
Java集合(个人整理笔记)
目录 1. 常见的集合有哪些? 2. 线程安全的集合有哪些?线程不安全的呢? 3. Arraylist与 LinkedList 异同点? 4. ArrayList 与 Vector 区别? 5. Array 和 ArrayList 有什么区别?什么时候该应 Array而不是…...
Redis -- 缓存穿透问题解决思路
缓存穿透 :缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。 常见的解决方案有两种: 缓存空对象 优点:实现简单,维护方便 缺点: 额外…...
数据挖掘中的PCA和KMeans:Airbnb房源案例研究
目录 一、PCA简介 二、数据集概览 三、数据预处理步骤 四、PCA申请 五、KMeans 聚类 六、PCA成分分析 七、逆变换 八、质心分析 九、结论 十、深入探究 10.1 第 1 步:确定 PCA 组件的最佳数量 10.2 第 2 步:使用 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…...
IP归属地在互联网行业中的应用
摘要:IP(Internet Protocol)地址归属地是指互联网上某个IP地址所对应的地理位置信息。在互联网行业中,IP归属地具有重要的应用价值,包括网络安全、广告定向、用户定位等方面。IP数据云将探讨IP归属地在互联网行业中的应…...
非关系型数据库-----------探索 Redis高可用 、持久化、性能管理
目录 一、Redis 高可用 1.1什么是高可用 1.2Redis的高可用技术 二、 Redis 持久化 2.1持久化的功能 2.2Redis 提供两种方式进行持久化 三、Redis 持久化之----------RDB 3.1触发条件 3.1.1手动触发 3.1.2自动触发 3.1.3其他自动触发机制 3.2执行流程 3.3启动时加载…...
每日一题:三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 示例 1…...
【SCI绘图】【曲线图系列2 python】多类别标签对比的曲线图
SCI,CCF,EI及核心期刊绘图宝典,爆款持续更新,助力科研! 本期分享: 【SCI绘图】【曲线图系列2 python】多类别标签对比的曲线图,文末附完整代码。 1.环境准备 python 3 import proplot as pp…...
达梦DMHS-Manager工具安装部署
目录 1、前言 1.1、平台架构 1.2、平台原理 2、环境准备 2.1、硬件环境 2.2、软件环境 2.3、安装DMHS 2.3.1、源端DMHS前期准备 2.3.2、源端DMHS安装 2.3.3、目的端DMHS安装 3、DMHS-Manager客户端部署 3.1、启动dmhs web服务 3.2、登录web管理平台 4、添加DMHS实…...
Marketo营销自动化集成Zoho CRM
Marketo 本身是一种营销自动化工具,可让您根据指定的标准对潜在客户进行评分,并确定哪些潜在客户最有可能进行转化。 CRM 和 Marketo 之间的紧密集成可帮助您规划销售和营销活动,以培育这些高价值潜在客户并最大限度地提高您的团队可以赢得的…...
【Leetcode每日一题】模拟 - 外观数列(难度⭐⭐)(51)
1. 题目解析 题目链接:38. 外观数列 这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。 2.算法原理 所谓“外观数列”,其实只是依次统计字符串中连续且相同的字符的个数。依照题意,依次模拟即 可。…...
CMakeLists.txt编写简单介绍:CMakeLists.txt同时编译.cpp和.cu
关于CMakeLists.txt的相关介绍,这里不赘诉,本人的出发点是借助于CMakeLists.txt掌握基本的C++构建项目流程,下面是本人根据网络资料以及个人实践掌握的资料。 CMakeList.txt构建C++项目 下图是一个使用CUDA实现hello world的项目,一般来说,一个标准的C++项目包括三个文件…...
MSSQL有关数据库、表的循环操作可使用的存储过程 sp_MSforeachdb 及 sp_MSforeachtable
MSSQL有关数据库、表的循环操作可使用的存储过程: 1. sp_MSforeachdb command1print ?, command2DBCC CHECKDB(?) --检查所有的数据库 2. sp_MSforeachtable command1print ?, command2sp_spaceused ? --统计各个表的空间使用情况 【说明】sys.sp_MSforeachdb 和 …...
day63 单调栈part02
503. 下一个更大元素 II 中等 给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更…...
网站建站模式/5188关键词挖掘工具
企业级分布式关系型数据库TiDB 一、TiDB简介 TiDB 是 PingCAP 公司受 Google Spanner / F1 论文启发而设计的开源分布式关系型数据库,结合了传统的 RDBMS 和NoSQL 的最佳特性。TiDB 兼容 MySQL,支持无限的水平扩展,具备强一致性和高可用性。…...
wordpress bbs插件/深圳招聘网络推广
1. 前言:前几篇文章分享了下 MyBatis 拦截器的相关知识,这里再分享下自己项目中遇到的一个问题,然后通过自定义的拦截器快速的解决了问题。2. 项目用到的技术:SpringBoot,MyBatis.....3. 业务需求:最近项目…...
怎么做淘宝企业网站/婚恋网站排名前十名
&、|是操作数运算符,在if条件中不会造成短路;&运算布尔值时,只要有一个为false结果就为false,|运算布尔值时,只要有一个为true结果就为true。 &&、||是逻辑运算符,在if条件中会造成短路。…...
怎样判断网站的seo信息好坏/公司优化是什么意思?
设计一个算法,将字符串中的单词首尾翻转,例如: 输入:"abc def",那么输出为:"def abc",这里我采用的是使用两个指针从字符串尾部开始往前遍历,遇到间隔符便提…...
免费手机看电影app哪个软件好/宁波seo优化定制
1.Mybatis比IBatis比较大的几个改进是什么 a.有接口绑定,包括注解绑定sql和xml绑定Sql , b.动态sql由原来的节点配置变成OGNL表达式, c. 在一对一,一对多的时候引进了association,在一对多的时候引入了collection 节点,不过都是在resultMap里面…...
wordpress修改头部显示/青岛做网站推广
为什么80%的码农都做不了架构师?>>> MongoDB 1. 概念 SQL 与 NoSQL(MongoDB)的对比 关系型数据库 MongoDB 数据库 数据库 表 Table 集合 Collection 行 Row/Tuple 文档 Document 列 Column 字段 Field 表 Join 内嵌文档 Embedded Documen…...