重读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 框架中的重要作用。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/796511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity类银河恶魔城学习记录12-6.5 p128.5 Create item by Craft源代码

此章节在原视频缺失&#xff0c;此过程为根据源代码推断而来&#xff0c;并非原视频步骤 Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩…

yolov8实现用已经训练好的模型去实现数据集的自动标注

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、找到default.yaml文件二、修改default.yaml文件三、代码实现四、生成结果展示补充&#xff08;呼应前面代码训练数据集的路径位置&#xff09; 前言 我们经…

机器学习笔记 - 文字转语音技术路线简述以及相关工具不完全清单

一、TTS技术简述 今天的文本到语音转换技术(TTS)的目标已经不仅仅是让机器说话,而是让它们听起来像不同年龄和性别的人类。通常,TTS 系统合成器的质量是从不同方面进行评估的,包括合成语音的清晰度、自然度和偏好,以及人类感知因素,例如可理解性。 1、技术路线 (1)基…

【多模态融合】MetaBEV 解决传感器故障 3D检测、BEV分割任务

前言 本文介绍多模态融合中&#xff0c;如何解决传感器故障问题&#xff1b;基于激光雷达和相机&#xff0c;融合为BEV特征&#xff0c;实现3D检测和BEV分割&#xff0c;提高系统容错性和稳定性。 会讲解论文整体思路、模型框架、论文核心点、损失函数、实验与测试效果等。 …

详解 Redis 在 Centos 系统上的安装

文章目录 详解 Redis 在 Centos 系统上的安装1. 使用 yum 安装 Redis 52. 创建符号链接3. 修改配置文件4. 启动和停止 Redis 详解 Redis 在 Centos 系统上的安装 1. 使用 yum 安装 Redis 5 如果是Centos8&#xff0c;yum 仓库中默认的 redis 版本就是5&#xff0c;直接 yum i…

【Python】免费的图片/图标网站

专栏文章索引&#xff1a;Python 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 这里是我收集的几个免费的图片/图标网站&#xff1a; iconfont-阿里巴巴矢量图标库icon&#xff08;.ico&#xff09;INCONFINDER&#xff08;.ico&#xff09;

Android14之智能指针的弱引用、强引用、弱指针、强指针用法区别及代码实例(二百零五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

2024年学鸿蒙开发“钱”途无量……

随着科技的不断发展和智能设备的普及&#xff0c;鸿蒙系统作为华为自主研发的操作系统&#xff0c;正逐渐受到市场的关注。2024年&#xff0c;学鸿蒙开发是否有前途&#xff0c;成为了很多开发者关心的问题。本文将从多个角度分析鸿蒙系统的发展前景&#xff0c;以及学习鸿蒙开…

linux 迁移home目录以及修改conda中pip的目录

1&#xff09;sudo rsync -av /home/lrf /data/home/lrf 将/home目录下的文件进行负责&#xff08;假设机械硬盘挂载在/data目录下&#xff09; 2&#xff09;usermod -d /data/home/lrf -m lrf 修改用户$HOME变量 3&#xff09;vi /etc/passwd 查看对应用户的$HOME变量是否成…

4.2.k8s的pod-标签管理、镜像拉取策略、容器重启策略、资源限制、优雅终止

一、标签管理 1.标签在k8s中极其重要&#xff0c;大多数资源的相互关联就需要使用标签&#xff1b;也就是说&#xff0c;资源的相互关联大多数时候&#xff0c;是使用标签进行关联的&#xff1b; 2.其他作用&#xff0c;在k8s集群中&#xff0c;node节点的一些操作比如污点及污…

【Linux】UDP编程【下】{三版本服务器/编程常见问题}

文章目录 3.linux网络涉及到的协议栈4.三个版本的服务器4.1响应式4.2命令式4.3交互式1.启动程序2.运行结果 3.linux网络涉及到的协议栈 Linux网络协议栈是一个复杂而强大的系统&#xff0c;它负责处理网络通信的各种细节。下面是对Linux网络协议栈的详细介绍&#xff1a; 套接…

12-2-CSS 字体图标

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 CSS 字体图标1 字体图标的产生2 字体图标的优点3 字体图标的下载4 字体图标的…

蓝桥杯算法题:卡片换位

问题描述 你玩过华容道的游戏吗&#xff1f;这是个类似的&#xff0c;但更简单的游戏。 看下面 2 x 3 的格子 --------- | A | * | * | --------- | B | | * | --------- 1 2 3 4 5 在其中放 5 张牌&#xff0c;其中 A 代表关羽&#xff0c;B 代表张飞&#xff0c;* 代表士兵…

CLCD 流水线发布SpringBoot项目

目录 一、流水线 1.1 点击进入流水线 1.2 新建流水线 二、添加流水线 三、构建上传和构建镜像 ​编辑 四、Docker部署 一、流水线 1.1 点击进入流水线 1.2 新建流水线 二、添加流水线 三、构建上传和构建镜像 在构建上传里添加一个步骤&#xff1a;构建镜像&#xff0c;这…

【鸿蒙 HarmonyOS】@ohos.promptAction (弹窗)

一、背景 创建并显示文本提示框、对话框和操作菜单。 文档地址&#x1f449;&#xff1a;文档中心 说明 本模块首批接口从API version 9开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 该模块不支持在UIAbility的文件声明处使用&#xff0c;即…

GitHub入门与实践

ISBN: 978-7-115-39409-5 作者&#xff1a;【日】大塚弘记 译者&#xff1a;支鹏浩、刘斌 页数&#xff1a;255页 阅读时间&#xff1a;2023-08-05 推荐指数&#xff1a;★★★★★ 好久之前读完的了&#xff0c;一直没有写笔记。 这本入门Git的书籍还是非常推荐的&#xff0c;…

【使用 PyQt6-第03章】 部件 QPushButton、QCheckBox、QComboBox、QLabel 和 QSlider 小部件

目录 一、说明二、快速演示三、QLabel四、Q复选框五、QComboBox六、QListWidget七、QLine编辑八、QSpinBox 和 QDoubleSpinBox九、Q滑块十、QDial十一、结论 一、说明 部件 QPushButton、QCheckBox、QComboBox、QLabel 和 QSlider 小部件 创建附加窗口&#xff0c;本教程也适…

Golang | Leetcode Golang题解之第14题最长公共前缀

题目&#xff1a; 题解&#xff1a; func longestCommonPrefix(strs []string) string {if len(strs) 0 {return ""}isCommonPrefix : func(length int) bool {str0, count : strs[0][:length], len(strs)for i : 1; i < count; i {if strs[i][:length] ! str0 …

HTTP 摘要认证

文章目录 一、什么是摘要认证二、工作流程三、实例演示 一、什么是摘要认证 摘要认证&#xff0c;即 Digest Access Authentication&#xff0c;是一种HTTP身份验证机制&#xff0c;用于验证用户的身份。相较于基本认证&#xff08;Basic Authentication&#xff09;使用用户名…

Qt之信号和槽的机制

前言 在 C 中&#xff0c;对象与对象之间产生联系要通过调用成员函数的方式。但是在 Qt中&#xff0c;Qt提供了一种新的对象间的通信方式&#xff0c;即信号和槽机制。在GUI编程中&#xff0c;通常希望一个窗口部件的一个状态的变化会被另一个窗口部件知道&#xff0c;为…