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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

Vue3从入门到实战:掌握状态管理库pinia(上部分)

1.新的状态管理工具pinia Pinia 是一个状态管理库&#xff0c;通俗点讲&#xff0c;它的主要作用就是帮助我们在 Vue 3 应用中更好地管理和维护组件的状态。 举例子解释&#xff1a; 新建一个Count.vue文件&#xff0c;功能用来计数求和。 <template><div class&q…

【MATLAB】哈里斯鹰优化(HHO)

发表在中科院二区Future Generation Computer Systems期刊上的论文“Harris hawks optimization: Algorithm and applications" 01.引言 本文提出了一种基于种群的、受自然启发的优化范式&#xff0c;称为Harris Hawks Optimizer (HHO)。HHO的主要灵感来源于自然界中哈里…

day64 单调栈part03

柱状图中最大的矩形 困难 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 看了一圈 最后还是别的题解几句话看明白了 明确一点&#xff0c;遍历每个高…

004 CSS介绍2

文章目录 css最常用属性link元素进制css颜色表示浏览器的渲染流程(不含js) css最常用属性 font-size 文字大小 color:前景色(文字颜色) background-color:背景色 width:宽度 height:高度 link元素 也可以用来创建站点图标 link元素常见属性 href:指定被链接资源的URL rel:指…

Proxifier监控流量转发Charles

Charles开启 SOCKS Proxy 在Proxifier的Proxy Servers选项中点击add添加如下规则&#xff1a;127.0.0.1:8889&#xff0c; 端口号8889对应Charles开放的端口号&#xff0c; 选择SOCKS5&#xff0c;支持监控http和https 配置好之后点击Check进行验证, 点击Test Settings选项&…

二.音视频编辑-媒体组合-播放

引言 当涉及到音视频编辑时&#xff0c;媒体资源的提取和组合是至关重要的环节。在iOS平台上&#xff0c;AVFoundation框架提供了丰富而强大的功能&#xff0c;使得媒体资源的操作变得轻松而高效。从原始的媒体中提取片段&#xff0c;然后将它们巧妙地组合成一个完整的作品&am…

SV学习笔记(二)

接口 什么是接口&#xff1f; 接口 主要用作验证 &#xff0c;国外有些团队会使用sv进行设计&#xff0c;那么接口就会用作设计。验证环境中&#xff0c;接口可以 使连接变得简洁而不易出错 。interface和module的使用性质很像&#xff0c; 可以定义端口&#xff0c;也可以定…

文心一言 VS 讯飞星火 VS chatgpt (230)-- 算法导论17.1 1题

一、如果栈操作包括 MULTIPUSH 操作&#xff0c;它将 k 个数据项压入栈中&#xff0c;那么栈操作的摊还代价的界还是 O(1) 吗? 文心一言&#xff1a; 在栈的基本操作中&#xff0c;我们通常考虑的是单个元素的压入&#xff08;push&#xff09;和弹出&#xff08;pop&#x…

升级程序到Java21的记录二(修改程序源代码)

背景 :参看 《升级程序到Java21的记录一&#xff08;先升级jdk到21&#xff09;》&#xff0c; Jdk升级完毕&#xff0c;下面我们该秀修改程序源代码了&#xff0c; 程序最初使用的springboot2.6.8 以及jdk17。为了使用springboot 3.0&#xff08;3.0开始有支持虚拟线程的相关…

抖音运营技巧

1、视频时长 抖音的作品是否能够继续被推荐&#xff0c;取决于综合数据&#xff0c;包括完播率、点赞率、评论率、转发率和收藏率等。其中&#xff0c;完播率是最容易控制的因素。对于新号来说&#xff0c;在没有粉丝的初期&#xff0c;发布过长的视频可能会导致无人观看。因此…

Day31|贪心算法part01:理论基础、455.分发饼干、376. 摆动序列、53. 最大子序和

理论基础 记得贪心没有规律即可&#xff01;解不出来就看题解。 455. 分发饼干 先把学生和饼干都排序&#xff08;Arrays.sort只能升序&#xff09;&#xff0c;然后都从后往前遍历&#xff0c;把最大的饼干给需求最大的孩子&#xff08;贪心&#xff09; class Solution {…

4核8G服务器配置性能怎么样?4核8G12M配置服务器能干啥?

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

内网安全之-kerberos协议

kerberos协议是由麻省理工学院提出的一种网络身份验证协议&#xff0c;提供了一种在开放的非安全网络中认证识别用户身份信息的方法。它旨在通过使用秘钥加密技术为客户端/服务端应用提供强身份验证&#xff0c;使用kerberos这个名字是因为需要三方的共同参与才能完成一次认证流…

【C++】stack和queue

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. stack的介绍和使用1.1 stack的介绍1.2 stack的使用1.3 stack的模拟实现 2. queue的介绍和使用2.1 queue的介绍2.2 queue的使用2.3 queue的模拟实现 3. 容器适配器3.1 概念3.2 STL标准库中stack和queue的底层结构3.…

@RequestParam和@PathVariable的区别

同样都是接收URL中的参数&#xff0c;RequestParam和PathVariable有什么区别呢&#xff1f;

随手集☞Spring知识盘点

概述 定义 Spring框架的提出者是程序员Rod Johnson&#xff0c;他在2002年最早提出了这个框架的概念&#xff0c;随后创建了这个框架。Spring框架的目标是简化企业级Java应用程序的开发&#xff0c;通过提供一套全面的工具和功能&#xff0c;使开发者能够更加高效地构建高质量…

Prometheus+grafana环境搭建MongoDB(docker+二进制两种方式安装)(五)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前四篇mongodb的exporter坑也挺多总结一下各种安装方式&#xff0c;方便后续考古。 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabb…

5分钟润色一篇论文:ChatGPT意味着什么?Nature连发两篇文章探讨

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

服务器硬件构成与性能要点:CPU、内存、硬盘、RAID、网络接口卡等关键组件的基础知识总结

文章目录 服务器硬件基础知识CPU&#xff08;中央处理器&#xff09;内存&#xff08;RAM&#xff09;硬盘RAID&#xff08;磁盘阵列&#xff09;网络接口卡&#xff08;NIC&#xff09;电源散热器主板显卡光驱 服务器硬件基础知识 服务器是一种高性能计算机&#xff0c;用于在…