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

news2024/12/23 22:52:47

引言

在软件开发中,经常会遇到不同接口之间的兼容性问题。当需要使用一个已有的类,但其接口与我们所需的不兼容时,我们可以通过适配器模式来解决这一问题。适配器模式是一种结构型设计模式,它允许接口不兼容的类之间进行合作。本文将深入探讨适配器模式的概念、应用场景以及在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 {
    @Override
    public void request() {
        specificRequest();
    }
}

3.2 对象适配器

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

// 对象适配器
class Adapter implements Target {
    private Adaptee adaptee;

    Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public 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() {

  }

  @Override
  public 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 {
  @Override
  public void hdmi() {
    System.out.println("数据线插入 HDMI 接口成功!");
  }

  @Override
  public void usb() {
    throw new UnsupportedOperationException("该数据线不允许插入当前插槽");
  }

  @Override
  public String type() {
    return HDMI;
  }
}

public class USBSlot implements ComputerSlot {
  @Override
  public void hdmi() {
    throw new UnsupportedOperationException("该数据线不允许插入当前插槽");
  }

  @Override
  public void usb() {
    System.out.println("数据线插入 USB 接口成功!");
  }

  @Override
  public String type() {
    return USB;
  }
}

  • 目标接口
public interface AdvancedComputerSlot {

  String TYPEC = "TYPE-C";

  void typeC(String type);
}

public class AdvancedComputerSlotImpl implements AdvancedComputerSlot {

  ComputerAdapter adapter = new ComputerAdapter();

  @Override
  public 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
	 */
	@Nullable
	ModelAndView 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}.
	 */
	@Deprecated
	long getLastModified(HttpServletRequest request, Object handler);

}
public class HttpRequestHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	@Nullable
	public 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 {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public 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.coloradmin.cn/o/1571301.html

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

相关文章

使用Python转换图片中的颜色

说明&#xff1a;最近在看梵高的画册&#xff0c;我手上的这本画册&#xff08;《文森特梵高》杨建飞 主编&#xff09;书中说&#xff0c;梵高用的颜料里有不耐久的合成颜料&#xff0c;原本的紫色褪成了我们现在所看到的灰蓝色。于是我想&#xff0c;能不能用程序将画中的颜色…

Javascript 插值搜索-迭代与递归(Interpolation Search)

给定一个由 n 个均匀分布值 arr[] 组成的排序数组&#xff0c;编写一个函数来搜索数组中的特定元素 x。 线性搜索需要 O(n) 时间找到元素&#xff0c;跳转搜索需要 O(? n) 时间&#xff0c;二分搜索需要 O(log n) 时间。 插值搜索是对实例二分搜索的改进&#xff0c;…

AI绘画:使用ComfyUI结合LCM进行实时绘图:开启AI艺术创作新篇章

在数字艺术的世界里&#xff0c;ComfyUI和LCM&#xff08;Latent Contextual Modulation&#xff09;的结合为艺术家和设计师们提供了一个强大的实时绘图工具。LCM是一种先进的技术&#xff0c;它能够实时地将用户的输入和反馈融入到图像生成过程中&#xff0c;从而创造出动态变…

Web3 革命:揭示区块链技术的全新应用

随着数字化时代的不断发展&#xff0c;区块链技术作为一项颠覆性的创新正在改变着我们的世界。而在这一技术的进步中&#xff0c;Web3正逐渐崭露头角&#xff0c;为区块链技术的应用带来了全新的可能性。本文将探讨Web3革命所揭示的区块链技术全新应用&#xff0c;并展望其未来…

第1讲——预备知识

一、视觉SLAM十四讲在讲些啥 SLAM&#xff1a;Simultaneous Localization and Mapping 翻译&#xff1a;同时定位与地图构建 搭载特定传感器的主体&#xff0c;在没有环境先验信息的情况下&#xff0c;于运动过程中建立环境的模型&#xff0c;同时估计自己的运动。 当特定传感…

构建开源可观测平台

企业始终面临着确保 IT 基础设施和应用程序全年可用的压力。现代架构&#xff08;容器、混合云、SOA、微服务等&#xff09;的复杂性不断增长&#xff0c;产生大量难以管理的日志。我们需要智能应用程序性能管理 (APM) 和可观察性工具来实现卓越生产并满足可用性和正常运行时间…

基于SpringBoot+微信小程序的防诈骗平台

一、项目背景介绍&#xff1a; 社会背景随着互联网的高速发展&#xff0c;网络和手机的普及率也大大提高&#xff0c;这也衍生出一系列问题&#xff1a;用户信息泄露、不法分子电话诈骗等…现越来越多的老年人甚至年轻人经历过电信诈骗并被骗了大量金额。该产品正是基于这样的社…

揭秘SCQL:隐私计算的未来之路

1.SCQL使用/集成最佳实践 隐语隐私计算中SCQL&#xff08;Secure Collaborative Query Language&#xff09;的设计旨在提供一种便捷且安全的方式来处理多方参与下的隐私敏感数据查询与分析&#xff0c;而无需暴露原始数据给任何一方。以下是基于以上所记录信息的SCQL使用和集…

Linux文件IO(3):使用文件IO进行文件的打开、关闭、读写、定位等相关操作

目录 1. 文件IO的概念 2. 文件描述符概念 3. 函数介绍 3.1 文件IO-open函数 3.2 文件IO-close函数 3.3 文件IO-read函数 3.4 文件IO-write函数 3.5 文件IO-lseek函数 4. 代码练习 4.1 要求 4.2 具体实现代码 4.3 测试结果 5. 总结 1. 文件IO的概念 posix(可移植操作系统接…

vulhub中Struts2-008 远程代码执行漏洞复现

影响版本: 2.1.0 - 2.3.1 原理 > S2-008 涉及多个漏洞&#xff0c;Cookie 拦截器错误配置可造成 OGNL 表达式执行&#xff0c;但是由于大多 Web 容器&#xff08;如 Tomcat&#xff09;对 Cookie 名称都有字符限制&#xff0c;一些关键字符无法使用使得这个点显得比较鸡肋…

深入了解 Python 中标准排序算法 Timsort

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ Timsort&#xff1a;一个非常快速的、时间复杂度为 O ( n l o g n ) O (n \ log\ n) O(n log n)、稳健&#xff08;即不改变等值元素间的相对顺序&#xff09;的排序算法&#xff0c;在处理真实世界数…

ModuleNotFoundError: No module named ‘einops‘解决办法

安装对应的库就好 pip install einops -i https://pypi.tuna.tsinghua.edu.cn/simple 拓展——在python中einops模块有什么作用 einops 是一个 Python 库&#xff0c;它提供了一种简洁、易读的方式来操作多维数组&#xff08;通常是 NumPy 数组或 PyTorch 张量&#xff09;。e…

移动端基础

移动端基础 一.了解二.视口1.视口形式2.视口标签3.viewport设置 三.二倍图1.像素比2.多倍图3.背景缩放及使用&#xff08;background-size&#xff09;4.多倍图切图 四.移动端开发选择1.单独制作2.响应式3.总结 五.移动端技术解决方案1.初始化2.盒子模型3.特殊样式 六.常见布局…

铸铁平台的平面度

铸铁平台的平面度是指平台的表面平整程度&#xff0c;即平台表面与其理论平面之间的最大偏差。平台的平面度通常使用国际标准符号GD&T中的平面度符号&#xff08;ⓨ&#xff09;表示&#xff0c;单位为毫米&#xff08;mm&#xff09;或微米&#xff08;μm&#xff09;。 …

【数据分析面试】10. 计算平均通勤时间(SQL:timestampdiff() 和datediff()区别)

题目 假设你在Uber工作。rides表包含了关于Uber用户在美国各地的行程信息。 编写一个查询&#xff0c;以获取纽约&#xff08;NY&#xff09;每位通勤者的平均通勤时间&#xff08;以分钟为单位&#xff09;&#xff0c;以及纽约所有通勤者的平均通勤时间&#xff08;以分钟为…

Codeforces Round 836 (Div. 2) D. Range = √Sum

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5; c…

Phpstorm配置Xdebug

步骤 1、先去官网找到对应的php xdebug的版本 2、配置phpstorm断点调试 网址&#xff1a;https://xdebug.org/ 查看php对应的xdebug版本&#xff1a;Xdebug: Support — Tailored Installation Instructions 1.1查看对应php xdebug版本 全选&#xff0c;复制到目标网址 我…

数据恢复软件有哪些?适用于 Windows PC 的 10 款最佳免费数据恢复软件

数据已成为数字世界运作的主要来源。任何数据丢失都会对公司的日常活动产生巨大影响。它会影响过程的连续性。以下文章为您带来了各种简单且免费使用的数据恢复软件。 什么是数据恢复&#xff1f; 检索和恢复丢失、损坏、无法访问、损坏或意外删除的数据的过程称为数据恢复。这…

B3631 单向链表(结构体模拟链表)

输入格式 第一行一个整数 q表示操作次数。 接下来 q行&#xff0c;每行表示一次操作&#xff0c;操作具体间题目描述。 输出格式 对于每个操作 2&#xff0c;输出一个数字&#xff0c;用换行隔开。 #include<iostream> #include<map> #include<algorithm> …

【随笔】Git 高级篇 -- 整理提交记录(上)(十五)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…