设计模式——适配器模式

引入实例

说起适配器其实在我们的生活中是非常常见的,比如:学校的宿舍的电压都比较低,而有的学生想使用大功率电器,宿舍的就会跳闸,然而如果你使用一个适配器(变压器)就可以使用了(温馨提示宿舍使用大功率电器不太安全,容易引起火灾,希望大家谨慎使用)。

又比如说,有的插座都是三孔的,而我们用的大部分电器是两孔的,这时我们可以使用一个适配器,适配器本身是三孔的,它可以直接插到三孔的插头上,适配器本身可以提供一个两孔的插座,然后我们就可以插到适配器上了,这样我们原本只能插到两孔上的插头就能用三孔的插座了。

适配器模式的相关概念

适配器模式的正式定义

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

适配器模式分为

  • 类适配器模式
  • 对象适配器模式

适配器模式的结构

适配器模式里面总共拥有三个角色,它们分别是:

  • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  • 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  • 适配器(Adapter)类:它是一个转换器,通过继承(类适配器模式)或引用适配者的对象(对象适配器模式),把适配者接口转换成目标接口(也就是使用转换器将三头的插座转换成适合我们使用的两头插座),让客户按目标接口的格式访问适配者。

注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

类适配器

类适配器的原理就是通过继承来实现适配器功能。

让Adapter实现Target接口,并且继承Adaptee,这样Adapter就具备Target和Adaptee的特性,就可以将两者进行转化。

举例:以不同设备使用不同交流电为例,通过电源适配器进行转换说明。

创建目标角色(Target)

public interface Target {
    int out();
}

创建源角色(Adaptee)

public class Adaptee{
    public int input() {
        System.out.println("输入交流电: 220V");
        return 220;
    }
}

创建适配器(Adapter)

public class Adapter extends Adaptee implements Target {
    @Override
    public int out() {
        int input220V = super.input();
        int output = input220V / 2;
        System.out.println("输出交流电: " + output + "V");
        return output;
    }
}

客户端调用

public static void main(String[] args) {
    Target adapter = new Adapter();
    int result = adapter.out();
    System.out.println(result);
}

输入交流电: 220V
输出交流电: 110V
110

对象适配器

对象适配器的原理就是通过组合来实现适配器功能。

让Adapter实现Target接口,然后内部持有Adaptee实例,然后再Target接口规定的方法内转换Adaptee。

创建目标角色(Target)

public interface Target {
    int out();
}

创建源角色(Adaptee)

public class Adaptee{
    public int input() {
        System.out.println("输入交流电: 220V");
        return 220;
    }
}

创建适配器(Adapter)

public class Adapter implements Target {
    private Adaptee adaptee;
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    @Override
    public int out() {
        int output = adaptee.input() / 2;
        System.out.println("输出交流电: " + output + "V");
        return output;
    }
}

客户端调用

    public static void main(String[] args) {
        Target adapter = new Adapter(new Adaptee());
        int result = adapter.out();
        System.out.println(result);
    }

输入交流电: 220V
输出交流电: 110V
110

接口适配器

接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,类显得很臃肿。此时,使用接口适配器就能让我们只实现我们需要的接口方法,目标更清晰。

接口适配器的主要原理就是原理利用抽象类实现接口,并且空实现接口众多方法。

创建目标角色(Target)

public interface Target {
    int out1();
    int out2();
    int out3();
    int out4();
}

创建源角色(Adaptee)

public class Adaptee{
    public int input() {
        System.out.println("输入交流电: 220V");
        return 220;
    }
}

创建适配器(Adapter)

public class Adapter implements Target {
    protected Adaptee adaptee;
    public Adapter(Adaptee adaptee){
        this.adaptee = adaptee;
    }
    @Override
    public int out1() {
        int input220V = adaptee.input();
        int output = input220V / 1;
        System.out.println("输出交流电: " + output + "V");
        return output;
    }
    @Override
    public int out2() {
        int input220V = adaptee.input();
        int output = input220V / 2;
        System.out.println("输出交流电: " + output + "V");
        return output;
    }
    @Override
    public int out3() {
        return 0;
    }
    @Override
    public int out4() {
        return 0;
    }
}

客户端调用

    public static void main(String[] args) {
        Target adapter = new Adapter(new Adaptee());
        adapter.out1();
        System.out.println("---------------------");
        adapter.out2();
        System.out.println("---------------------");
        Target adapter2 = new Adapter(new Adaptee()) {
            @Override
            public int out3() {
                int input220V = adaptee.input();
                int output = input220V / 3;
                System.out.println("输出交流电: " + output + "V");
                return output;
            }
        };
        adapter2.out3();
        System.out.println("---------------------");
        Target adapter3 = new Adapter(new Adaptee()) {
            @Override
            public int out4() {
                int input220V = adaptee.input();
                int output = input220V / 4;
                System.out.println("输出交流电: " + output + "V");
                return output;
            }
        };
        adapter3.out4();
    }

输入交流电: 220V
输出交流电: 220V
---------------------
输入交流电: 220V
输出交流电: 110V
---------------------
输入交流电: 220V
输出交流电: 73V
---------------------
输入交流电: 220V
输出交流电: 55V

优缺点

适配器模式优点:

  • 可以让任何两个没有关联的类一起运行。
  • 提高了类的复用。
  • 增加了类的透明度。
  • 灵活性好。

适配器模式缺点:

  • 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
  • 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

优点 :

  • 类适配器模式优点:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
  • 对象适配器模式优点:一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

缺点:

  • 类适配器模式缺点:对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。
  • 对象适配器模式缺点:与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

类适配器和对象适配器的区别:

  • 前者类之间的耦合度比后者高(这是因为类适配器模式使用的是继承的方式,而对象适配器模式使用的是聚合或者组合的方式)。
  • 类适配器模式要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些,用的更多的还是对象适配器模式。

应用场景

  1. 当一个系统需要使用另一个系统的接口时,但是两个系统的接口不兼容,可以使用适配器模式进行接口转换。
  2. 在数据处理方面,适配器模式可以用于将不同格式的数据适配到一个标准的数据格式上。
  3. 当我们需要复用一些已有的类的时候,这些类的接口与我们需要的接口不兼容,可以使用适配器将这些类的接口转换成我们需要的接口,从而实现类的复用。
  4. 适配器模式可以用于封装有缺陷的接口设计,使得客户可以通过适配器来使用这些缺陷的接口,而无需直接与其交互。
  5. 替换依赖的外部系统,使得系统可以在不修改源代码的情况下适应新的外部系统。

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

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

相关文章

搭建openGauss 5.0 一主一从复制集群

openGauss是一款支持SQL2003标准语法,支持主备部署的高可用关系型国产数据库。 多种存储模式支持复合业务场景,新引入提供原地更新存储引擎。NUMA化数据结构支持高性能。Paxos一致性日志复制协议,主备模式,CRC校验支持高可用。支…

golang操作excel的高性能库——excelize/v2

目录 介绍文档与源码安装快速开始创建 Excel 文档读取 Excel 文档打开数据流流式写入 [相关 Excel 开源类库性能对比](https://xuri.me/excelize/zh-hans/performance.html) 介绍 Excelize是一个纯Go编写的库,提供了一组功能,允许你向XLAM / XLSM / XLS…

【【STM32之GPIO】】

STM32之GPIO 学完了正点原子自带的视频课之后感觉仍然一知半解现在更新一下来自其他版本的STM32学习 GPIO 就是 General Purpose Input Output 中文名叫通用输入输出口 可配置8种输入输出模式 引脚电平 0V~3.3V 部分引脚可容忍5V 输出模式下可控制端口输出高低电平&#xff…

【Spring Cloud 二】——Spring Cloud基本介绍

Spring Cloud基本介绍 一、Spring Cloud简介二、Spring Cloud核心组件Spring Cloud Netflix组件Spring Cloud Alibaba组件Spring Cloud原生组件微服务架构图 三、Spring Cloud与Spirng Boot的关系四、Spring Cloud的版本选择Spring Cloud Alibaba的版本选择 一、Spring Cloud简…

从外部访问K8s中Pod的五种方式

hostNetwork、 hostPort、 NodePort、 LoadBalancer、 Ingress 暴露Pod与Service一样,因为Pod就是Service的backend 1、hostNetwork:true 这是一种直接定义 Pod 网络的方式。 如果在 Pod 中使用 hostNetwork:true 配置, pod 中运行的应用程序…

UG\NX 二次开发 相切面、相邻面的选择控件

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan 简介: 有群友问“UFUN多选功能过滤面不能选择相切面或相邻面之类的吗?” 这个用Block UI的"面收集器"就可以,ufun函数是不行的。 效果: C++语言在UG二次开发中的应用及综合分析 C++ …

vue技术学习

vue快速入门 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>vue快速入门</title> </head> <body> <!--老师解读 1. div元素不是必须的&#xff0c;也可以是其它元素&#xff0…

第三章nginx详解

nginx&#xff1a;高性能&#xff0c;轻量级的web服务软件。 特点&#xff1a; 1&#xff0c;稳定性高。&#xff08;没有apache稳定&#xff09; 2&#xff0c;系统资源消耗地较低。&#xff08;处理http请求的并发能力非常高&#xff0c;单台物理服务器可以处理30000-5000…

Azure创建可用性集

什么是可用性集 在Azure中&#xff0c;可用性集&#xff08;Availability Set&#xff09;是一种用于提高虚拟机&#xff08;VM&#xff09;可用性和可靠性的功能。它通过将虚拟机分布在不同的物理硬件和故障域中来提供高可用性。每个故障域都是一个独立的电力和网络故障区域&…

论文笔记:Continuous Trajectory Generation Based on Two-Stage GAN

2023 AAAI 1 intro 1.1 背景 建模人类个体移动模式并生成接近真实的轨迹在许多应用中至关重要 1&#xff09;生成轨迹方法能够为城市规划、流行病传播分析和交通管控等城市假设分析场景提供仿仿真数据支撑2&#xff09;生成轨迹方法也是目前促进轨迹数据开源共享与解决轨迹数…

WSL2 ubuntu子系统OpenCV调用本机摄像头的RTSP视频流做开发测试

文章目录 前言一、Ubuntu安装opencv库二、启动 Windows 本机的 RTSP 视频流下载解压 EasyDarwin查看本机摄像头设备开始推流 三、在ubuntu 终端编写代码创建目录及文件创建CMakeLists.txt文件启动 cmake 配置并构建 四、结果展示启动图形界面在图形界面打开终端找到 rtsp_demo运…

Python标准库-追踪异常,定位问题-traceback

在日常的编程过程中&#xff0c;我们经常会遇到各种错误和异常。而当程序发生异常时&#xff0c;了解如何有效地追踪异常信息并定位问题&#xff0c;是每个开发者必备的技能之一。 Python 提供了一个强大的工具&#xff0c;称为 Traceback&#xff0c;它可以帮助我们跟踪异常的…

Midjourney Prompt 提示词速查表 v5.2

Midjourney 最新的版本更新正不断推出令人兴奋的新功能。这虽然不断扩展了我们的AI绘图工具箱&#xff0c;但有时也会让我们难以掌握所有实际可以使用的功能和参数。 针对此问题, 小编整理了 "Midjourney Prompt 提示词速查表"&#xff0c;这是一个非常方便的 Midjo…

Sentinel 规则持久化

文章目录 Sentinel 规则持久化一、修改order-service服务1.引入依赖2.配置nacos地址 第二步修改非常麻烦&#xff0c;可以略过&#xff0c;直接使用已经打好包的来使用二、修改sentinel-dashboard源码1. 解压2. 修改nacos依赖3. 添加nacos支持4. 修改nacos地址5. 配置nacos数据…

[gdc23]《战神:诸神黄昏》中的积雪系统

overview gdc23上santa monica带来基于tesselation的displacement map的可交互积雪系统&#xff0c;这是一个对于前作&#xff08;战神4&#xff09;的screen space parallax mapping的升级&#xff0c;而且是一个由自身render programmer在一个项目周期内&#xff0c;完成的&…

【探索Linux】—— 强大的命令行工具 P.6(调试器-gdb、项目自动化构建工具-make/Makefile)

阅读导航 前言一、什么是调试器二、详解 GDB - 调试器1.使用前提2.经常使用的命令3.使用小技巧 三、项目自动化构建工具 - make/Makefile1. make命令⭕语法⭕常用选项⭕常用操作⭕make命令的工作原理⭕make命令的优势&#xff1a; 2.Makefile文件⭕Makefile的基本结构⭕Makefil…

MongoDB增删改查操作

数据库操作&#xff1a; 在MongoDB中&#xff0c;文档集合存在数据库中。 要选择使用的数据库&#xff0c;请在mongo shell程序中发出 use <db> 语句 // 查看有哪些数据库 show dbs;// 如果数据库不存在&#xff0c;则创建并切换到该数据库&#xff0c;存在则直接切换到…

Visual Studio 如何放大代码字体的大小

1.打开Visual Studio&#xff0c;新建一个程序&#xff0c;一段代码&#xff0c;为接下去的操作做好准备。单击菜单栏的【工具】选项。 2.在跳出来菜单中找到【选项】&#xff08;一般在最后一项&#xff09;&#xff0c;然后单击。跳出新的窗口。 3.跳出新的窗口后&#xff…

频繁full gc 调参

Error message from spark is:java.lang.Exception: application_1678793738534_17900289 Driver Disassociated [akka.tcp://sparkDriverClient11.71.243.117:37931] <- [akka.tcp://sparkYarnSQLAM9.10.130.149:38513] disassociated! 日志里频繁full gc &#xff0c;可以…

408反向改考自命题的211学校,计算机招生近500人!今年能捡到漏吗?

贵州大学(C) 考研难度&#xff08;☆☆☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析。 正文1498字&#xff0c;预计阅读&#xff1a;3分钟。 2023考情概况 贵州大学计算机相关各…