深入浅出设计模式 - 适配器模式

news2024/12/25 1:47:29

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

在这里插入图片描述

文章目录

  • 1、什么是适配器模式
  • 2、适配器模式的优缺点
  • 3、适配器模式的应用场景
  • 4、适配器模式的结构
  • 5、类适配器模式的代码案例
  • 6、对象适配器模式的代码案例
  • 7、接口适配器模式的代码案例

1、什么是适配器模式

适配器模式是一种结构型设计模式,它用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式可以让原本由于接口不兼容而无法一起工作的类能够协同工作。

适配器模式的主要作用是解决两个已有接口之间的不兼容问题,以便它们能够协同工作。适配器模式有三种实现方式:类适配器、对象适配器和接口适配器。

类适配器是通过继承来实现的,它可以适配一个类的接口到另一个类的接口。对象适配器是通过组合来实现的,它可以适配一个对象的接口到另一个对象的接口。接口适配器是通过抽象类来实现的,它可以适配一个接口的部分方法到另一个接口。

适配器模式的优点是可以让原本不兼容的接口协同工作,提高代码的复用性和灵活性。缺点是可能会增加代码的复杂度和维护成本。

2、适配器模式的优缺点

适配器模式的优点:

  1. 可以让原本不兼容的接口协同工作,提高代码的复用性和灵活性。

  2. 可以将适配器类和目标类解耦,使得适配器类和目标类可以独立变化。

  3. 可以增加代码的可读性和可维护性,使代码更加清晰。

适配器模式的缺点:

  1. 可能会增加代码的复杂度和维护成本。

  2. 适配器模式需要增加一个额外的适配器类,增加了代码的量。

  3. 如果设计不当,可能会导致适配器类的滥用,增加代码的混乱程度。

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

适配器模式的应用场景:

  1. 旧接口适配新接口:当系统中的一个类需要使用另一个类的接口,但是这两个类的接口不兼容时,可以使用适配器模式。适配器模式可以将一个类的接口转换成系统需要的另一个接口,从而使得原本不兼容的类可以协同工作。

  2. 多个类的接口统一:当系统中的多个类的接口不统一时,可以使用适配器模式将它们的接口统一成一个接口。这样,这些类就可以协同工作,提高了系统的灵活性和可扩展性。

  3. 适配器模式还可以用于封装有缺陷的接口设计。如果接口的设计不完善,或者需要进行修改,但是又不能对客户端代码产生影响,可以使用适配器模式来封装这些缺陷接口,从而提供一个稳定的接口给客户端使用。

4、适配器模式的结构

适配器模式的结构包括以下几个角色:

  1. 目标接口(Target):定义客户端使用的接口,也就是客户端期望的接口。

  2. 源接口(Adaptee):定义需要适配的接口,也就是客户端现有的接口。

  3. 适配器(Adapter):实现目标接口,并持有一个源接口的引用,用于将客户端的请求转换成对源接口的调用。

  4. 客户端(Client):使用目标接口来调用适配器的方法,从而间接调用源接口的方法。

在适配器模式中,客户端通过调用目标接口来使用适配器的方法。适配器持有一个源接口的引用,用于将客户端的请求转换成对源接口的调用,并将返回结果转换成客户端期望的格式。这样,客户端就可以使用适配器来调用源接口的方法,而不需要了解源接口的实现细节。

5、类适配器模式的代码案例

以下是一个使用类适配器模式的示例代码:

假设我们有一个 Adaptee 类,它具有一个 specificRequest() 方法,但是它的接口与我们需要的 Target 接口不兼容。我们需要一个适配器来使得 Adaptee 类能够适配 Target 接口。

首先,我们定义一个 Target 接口:

package com.pany.camp.design.principle.adapter.clazz;

/**
 *
 * @description:  
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 16:57 
 */
public interface Target {
    void request();
}

然后,我们定义一个 Adaptee 类:

package com.pany.camp.design.principle.adapter.clazz;

/**
 *
 * @description:  Adaptee
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 16:58
 */
public class Adaptee {

    public void specificRequest() {
        System.out.println("Adaptee specific request");
    }
}

接下来,我们创建一个适配器类 Adapter ,它继承了 Adaptee 类,并实现了 Target 接口:

package com.pany.camp.design.principle.adapter.clazz;

/**
 *
 * @description:  适配器类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 16:59 
 */
public class Adapter extends Adaptee implements Target {
    @Override
    public void request() {
        specificRequest();
    }
}

在适配器类中,我们继承了 Adaptee 类,并实现了 Target 接口。在 request() 方法中,我们调用了 specificRequest() 方法,从而使得 Adaptee 类能够适配 Target 接口。

最后,我们可以使用适配器来调用 Target 接口的方法:

package com.pany.camp.design.principle.adapter.clazz;

/**
 *
 * @description:  客户端
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:00
 */
public class Client {
    public static void main(String[] args) {
        Target target = new Adapter();
        target.request();
    }
}

输出结果如下:

Adaptee specific request

Process finished with exit code 0

在客户端代码中,我们创建了一个适配器对象,并将其赋值给 Target 接口的引用。然后,我们调用了 request() 方法,实际上是调用了适配器的 request() 方法,从而间接调用了 Adaptee 类的 specificRequest() 方法。

6、对象适配器模式的代码案例

以下是一个使用对象适配器模式的示例代码:

假设我们有一个 Adaptee 类,它具有一个 specificRequest() 方法,但是它的接口与我们需要的 Target 接口不兼容。我们需要一个适配器来使得 Adaptee 类能够适配 Target 接口。

首先,我们定义一个 Target 接口:

package com.pany.camp.design.principle.adapter.object;

/**
 *
 * @description:  Target
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:03
 */
public interface Target {
    void request();
}

然后,我们定义一个 Adaptee 类:

package com.pany.camp.design.principle.adapter.object;

/**
 *
 * @description:  Adaptee
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:04
 */
public class Adaptee {
    
    public void specificRequest() {
        System.out.println("Adaptee specific request");
    }
}

接下来,我们创建一个适配器类 Adapter ,它实现了 Target 接口,并持有一个 Adaptee 对象的引用:

package com.pany.camp.design.principle.adapter.object;

/**
 *
 * @description:  适配器类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:04
 */
public class Adapter implements Target {
    private Adaptee adaptee;

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

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

在适配器类中,我们实现了 Target 接口,并持有一个 Adaptee 对象的引用。在 request() 方法中,我们调用了 Adaptee 对象的 specificRequest() 方法,从而使得 Adaptee 类能够适配 Target 接口。

最后,我们可以使用适配器来调用 Target 接口的方法:

package com.pany.camp.design.principle.adapter.object;

/**
 *
 * @description:  客户端类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:05
 */
public class Client {

    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

在客户端代码中,我们创建了一个 Adaptee 对象和一个适配器对象,并将适配器对象赋值给 Target 接口的引用。然后,我们调用了 request() 方法,实际上是调用了适配器的 request() 方法,从而间接调用了 Adaptee 类的 specificRequest() 方法。

7、接口适配器模式的代码案例

以下是一个简单的接口适配器模式的代码案例,使用Java语言实现:

  1. 定义目标接口
package com.pany.camp.design.principle.adapter.interfaces;

/**
 *
 * @description:  Target
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:16
 */
public interface Target {
    void request();
}
  1. 实现目标接口的具体类
package com.pany.camp.design.principle.adapter.interfaces;

/**
 *
 * @description:  具体类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:16
 */
public class ConcreteTarget implements Target {
    @Override
    public void request() {
        System.out.println("ConcreteTarget.request() is called");
    }
}
  1. 定义适配器接口
package com.pany.camp.design.principle.adapter.interfaces;

/**
 *
 * @description:  适配器接口
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:17
 */
public interface Adapter {
    void specificRequest();
}
  1. 实现适配器接口的具体类,同时持有目标接口的引用
package com.pany.camp.design.principle.adapter.interfaces;

/**
 *
 * @description:  适配器接口的具体类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:17
 */
public class ConcreteAdapter implements Adapter {

    private Target target;

    public ConcreteAdapter(Target target) {
        this.target = target;
    }

    @Override
    public void specificRequest() {
        System.out.println("ConcreteAdapter.specificRequest() is called");
        target.request();
    }
}
  1. 在客户端中使用适配器
package com.pany.camp.design.principle.adapter.interfaces;

/**
 *
 * @description:  客户端
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 17:20
 */
public class Client {
    public static void main(String[] args) {
        Target target = new ConcreteTarget();
        Adapter adapter = new ConcreteAdapter(target);
        adapter.specificRequest();
    }
}

在上面的代码中, ConcreteTarget 是目标接口的具体实现类, ConcreteAdapter 是适配器接口的具体实现类,并且持有一个目标接口的引用。在客户端中,我们创建了一个目标接口的实例和一个适配器接口的实例,然后通过适配器接口调用目标接口的方法。

在这里插入图片描述

💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊

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

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

相关文章

Presto(Trino)分布式(物理)执行计划的生成和调度

文章目录 1.前言2.物理执行生成(Stage)的生成2.1不同的调度分区策略2.1.1 Connector自己提供的分区策略2.1.2 Presto提供的Partition策略(SystemPartitioningHandle): 2.2 为Stage创建StageScheduler2.2.1 普通的非bucket表的TableScan StageSplit 放置策略解析 2.2…

UE5.1.1 c++从0开始(14.用C++写UMG类)

先在这里放一个链接防止第一次看的朋友们不知道我在讲什么:https://www.bilibili.com/video/BV1nU4y1X7iQ/ 这一段的教程不难,唯一新建的C类是UMG的一个类。这个类用来写绑定在ai身上的血条。 总结一下一共做了什么事情: 给ai写了一个血条…

LeetCode Java两个单链表相交的一系列问题

题目描述 单链表可能有环,也可能无环。给定两个单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。 请实现一个函数,如果两个链表相交,请返回相交的第一个节点;如果不相交,返回n…

Android 渐变背景色

目录 一、背景 二、渐变 2.1 线性渐变背景色 1.新建资源文件 2.编辑样式文件 3.使用 4.编辑样式参数说明 2.2 圆角按钮渐变背景色 2.3 放射渐变 2.4 扫描线渐变 一、背景 单纯的颜色背景已经不能够满足UI大佬们的发挥,渐变色背景无疑成了一个炫技的方向。现在…

chatgpt赋能python:Python调用同一个类中方法详解

Python调用同一个类中方法详解 在Python编程中,类是一种非常重要的概念,可用于组织和管理代码。在同一个类中,可以定义多个方法。本文将详细介绍如何调用同一个类中的方法。 什么是类方法? 在Python中,类方法是指类…

魔兽世界自己架设任务

在魔兽世界中,玩家可以使用游戏内的任务编辑器自己架设任务来增加游戏的乐趣和挑战性。以下是详细的步骤: 第一步:打开任务编辑器 玩家可以在游戏中按下“ESC”键,进入游戏设置页面。在这个页面中,有一个“编辑器”选…

DSL查询分类与全文检索查询

DSL查询分类 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括: 查询所有:查询出所有数据,一般测试用。例如:match_all全文检索(full text&#x…

Idea新建springboot项目遇到的问题及解决

1.更换阿里云 方法&#xff1a; 找到文件路径&#xff1a;Settings > Build,Execution,Deployment > Build Tools > Maven 如下图&#xff1a; 找到相应的settings文件 如果没有就新建一个同名文件&#xff0c;内容如下&#xff1a; <settings xmlns"h…

Gitlab回退到指定版本的方法与步骤

一、先根据分支获取代码 如下&#xff1a; 下载好后&#xff0c;通过右键菜单进入git bash here 就进入下面界面 去gitlab上面去寻找需要的faf0af86d24f7de73b024785ad864f36da4284e2 git reset --hard cf2a5283b9a79f8cf04b003d05cdd94b2b3ff166 执行命令“git push -f”&…

vue中对语句的语义进行比较

一、安装 string-similarity库 npm install string-similarity二、html <div><input type"text" v-model"string1" placeholder"文本1" /> </div> <div><input type"text" v-model"string2" p…

[计算机入门]了解键盘

2.1 了解键盘 键盘一般可以根据按键的功能进行分区&#xff0c;一般分为&#xff1a;主键盘区、小键盘区、控制键区、功能键区、指示灯区。下面介绍键盘的各个分区按键及功能。 2.1.1 主键盘区 主键盘区又叫打字键盘区或字符键区&#xff0c;具有标准英文打字机键盘的格式。…

【Java】Java 中的栈和堆内存

本文仅供学习参考&#xff01; 相关教程地址&#xff1a; https://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html https://www.developer.com/java/stack-heap-java-memory/ https://zhuanlan.zhihu.com/p/529280783 Java 数据类型在执行过程中存储在两种不同形式的内…

HOT24-回文链表

leetcode原题链接&#xff1a;回文链表 题目描述 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a…

Leetcode 刷题 动态规划

198. 打家劫舍 1. 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i] 2. 确定递推公式 dp[i] max(dp[i - 2] nums[i], dp[i - 1]); 3. dp数组如何初始…

分类预测 | MATLAB实现GA-BiLSTM遗传算法优化双向长短期记忆网络的数据多输入分类预测

分类预测 | MATLAB实现GA-BiLSTM遗传算法优化双向长短期记忆网络的数据多输入分类预测 目录 分类预测 | MATLAB实现GA-BiLSTM遗传算法优化双向长短期记忆网络的数据多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现GA-BiLSTM遗传算法优化双向长短…

【数据库】MySQL慢查询常用分析方法

系统慢慢越来越卡了&#xff0c;怎么定位系统慢的原因&#xff0c;大部分是因为服务器资源占用耗费高引起的&#xff0c;如CPU&#xff0c;内存和带宽等等。MySQL在日常开发工作中可能会遇到某个新功能在测试时需要很久才返回结果&#xff0c;这时就应该分析是不是慢查询导致的…

【javascript】2048小游戏

目录 什么是2048 游戏状态机 游戏界面绘制 3.1 界面 3.2 数字的背景颜色 分数逻辑 4.1 加分 4.2 更新最高分 方向控制逻辑 5.1 数组 5.2 随机数 5.3 初始化 5.4 判断数组是否全部填满 5.5 判断方格是否还能移动 5.6 上下左右的监听事件 5.7 移动 完整代码 …

微服务划分的姿势

我们知道微服务是一种理念&#xff0c;没有确切的定义和边界&#xff0c;好比设计原则&#xff0c;是属于抽象的概念。在定义不明确的情况下谈划分也是一种各说各话&#xff0c;具体问题需要具体分析&#xff0c;所以这篇文章谈到的划分也不是绝对标准&#xff0c;仅供参考。 有…

final, finally和finalize的区别

final、finally和finalize是Java中用于异常处理的关键字。每个关键字都有不同的功能。final是一个访问修饰符&#xff0c;finally是异常处理中的代码块&#xff0c;而finalize是Object类的方法。 除此之外&#xff0c;final、finally和finalize之间还存在许多区别。下面是final…

netty学习(1):1个客户端与服务器通信

1. 新建maven工程&#xff0c;添加netty依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"…