设计模式学习之——适配器模式

适配器模式(Adapter Pattern),又称作变压器模式(因为这两者都体现了“转换”或“适配”的核心概念),是一种结构型设计模式。它将一个类的接口转换成客户端所期望的另一种接口,从而使得原本因接口不匹配而无法协同工作的两个类能够一起工作。

一、工作原理

适配器模式的工作原理是通过创建一个适配器类,该类包含一个源接口和一个目标接口。适配器类将客户端请求转换为源接口可以理解的命令,并执行相应的操作。这样,客户端只需要与适配器交互,而不需要直接与源接口交互,从而实现了接口的转换和适配。

二、主要角色

  • 目标接口(Target):定义客户端使用的接口,也就是客户端期望的接口。
  • 适配器(Adapter):实现目标接口,并持有一个源接口的引用,用于将客户端的请求转换成对源接口的调用。
  • 源接口(Adaptee):需要被适配的接口,即系统中已经存在的、但接口与目标接口不兼容的类。
  • 客户端(Client):使用目标接口来调用适配器的方法,从而间接调用源接口的方法。

三、实现方式

适配器模式有两种主要的实现方式:对象适配器和类适配器。

  • 对象适配器

    • 符合组合复用原则,使用了委托机制。
    • 在适配器类中维护一个被适配者(源接口)的成员变量,通过该成员变量调用被适配者的方法。
  • 类适配器

    • 通过类的继承关系实现适配器模式。
    • 适配器类继承被适配者类,通过super关键字调用被适配者的方法。

(注:在实际开发中,推荐使用对象适配器模式,因为组合通常比继承更加灵活,且更符合开闭原则。)

四、适用场景

  • 功能正确但接口不匹配:对于之前开发好的类,其操作和返回值都是正确的,但其定义的方法接口无法调用。此时可以使用适配器模式,使该类与用户的接口匹配,让用户使用适配器的接口,间接调用该类。
  • 软件维护阶段:在软件维护时,出现操作和返回值类似但函数接口不同的情况,为了适配第三方系统的接口,可以使用适配器模式。
  • 多个类的接口统一:当系统中的多个类的接口不统一时,可以使用适配器模式将它们的接口统一成一个接口,使这些类能够协同工作,提高系统的灵活性和可扩展性。

五、优缺点

  • 优点

    • 提高代码的复用性和灵活性:通过适配器模式,可以让原本不兼容的接口协同工作。
    • 降低耦合:目标类和现有类(被适配者)解除耦合,降低了系统的耦合性,易于扩展和维护。
    • 符合开闭原则:如果需要修改或扩展功能,只需要修改适配器类即可,目标类和现有类各自独立,互不影响。
  • 缺点

    • 增加代码的复杂度和维护成本:适配器模式需要增加一个额外的适配器类,增加了代码的量。如果设计不当,可能会导致适配器类的滥用,增加代码的混乱程度。
    • 降低可读性:系统代码可读性可能会降低,因为调用系统接口时,如果调用的是适配器接口,还需要查找调用的是哪个现有类的实际接口。

六、代码示例

示例一:电压适配器

假设我们有一个输出220V电压的设备(Voltage220V类),但我们有一个只能接受5V电压的设备(如手机,Phone类)。此时,我们可以使用适配器模式来创建一个电压适配器(VoltageAdapter类)。

// 被适配类:表示输出220V电压的设备
public class Voltage220V {public int output220V() {int src = 220;System.out.println("电压=" + src + "伏");return src;}
}// 适配接口:表示需要适配到的5V电压接口
public interface IVoltage5V {int output5V();
}// 适配器类:将220V电压适配为5V电压
public class VoltageAdapter implements IVoltage5V {private Voltage220V voltage220V;public VoltageAdapter(Voltage220V voltage220V) {this.voltage220V = voltage220V;}@Overridepublic int output5V() {int srcV = voltage220V.output220V(); // 获取220V电压int dstV = srcV / 44; // 转换为5V电压(这里仅为示例,实际转换可能更复杂)return dstV;}
}// 客户端类:表示只能接受5V电压的设备,如手机
public class Phone {// 充电方法,接受5V电压接口public void charging(IVoltage5V iVoltage5V) {if (iVoltage5V.output5V() == 5) {System.out.println("电压为5v,可以充电");} else if (iVoltage5V.output5V() > 5) {System.out.println("电压大于5v,无法充电");}}
}// 客户端测试代码
public class Client {public static void main(String[] args) {Phone phone = new Phone();Voltage220V voltage220V = new Voltage220V();VoltageAdapter voltageAdapter = new VoltageAdapter(voltage220V);phone.charging(voltageAdapter); // 通过适配器为手机充电}
}

在这个示例中,VoltageAdapter类作为适配器,将Voltage220V类的220V电压输出转换为5V电压输出,从而满足了Phone类的充电需求。

示例二:三相插座与两相插座的适配器

假设我们有一个三相插座(ThreeElectricOutlet接口)和一个两相插座(TwoElectricOutlet接口),以及一个只能插入两相插座的电视机(Tv类)。此时,我们可以使用适配器模式来创建一个三相到两相的插座适配器(TreeElecricAdapter类)。

// 三相插座接口
public interface ThreeElectricOutlet {void connectElectricCurrent();
}// 两相插座接口
public interface TwoElectricOutlet {void connectElectricCurrent();
}// 电视机类,实现了两相插座接口
public class Tv implements TwoElectricOutlet {private String name;public Tv() {name = "电视机";}public Tv(String name) {this.name = name;}@Overridepublic void connectElectricCurrent() {System.out.println(name + "开始播放节目");}
}// 三相到两相的插座适配器
public class TreeElecricAdapter implements ThreeElectricOutlet {TwoElectricOutlet outlet;public TreeElecricAdapter(TwoElectricOutlet teo) {this.outlet = teo;}@Overridepublic void connectElectricCurrent() {outlet.connectElectricCurrent();}
}// 客户端测试代码
public class TestActivity {public static void main(String[] args) {ThreeElectricOutlet outlet;Tv tv = new Tv("长虹电视机");TreeElecricAdapter adapter = new TreeElecricAdapter(tv);outlet = adapter;outlet.connectElectricCurrent(); // 通过适配器为电视机供电}
}

题外:适配器模式为什么又被称为变压器模式

适配器模式(Adapter Pattern)之所以又被称为变压器模式,是因为这两者都体现了“转换”或“适配”的核心概念。以下是对这一命名由来的详细解释:

1、功能上的相似性
  • 适配器模式

    • 主要解决的是接口不匹配的问题。
    • 通过适配器类,将一个类的接口转换成客户端所期望的另一种接口,从而使得原本因接口不匹配而不能一起工作的两个类能够一起工作。
  • 变压器

    • 在电力系统中,变压器的主要功能是将一种电压等级的电能转换成另一种电压等级的电能。
    • 通过变换电压,使得不同电压等级的电力系统能够相互连接和传输电能。

从功能上来看,适配器模式和变压器都起到了“转换”或“适配”的作用,使得原本不兼容的双方能够协同工作。

2、命名上的直观性
  • 适配器

    • 这个名称直接描述了该模式的主要功能,即适配或转换接口。
    • 在计算机领域中,适配器通常用于连接不同规格或类型的设备,使它们能够相互通信或协同工作。
  • 变压器

    • 这个名称同样直观地描述了其转换电压的功能。
    • 在电力系统中,变压器是不可或缺的转换设备。

将适配器模式命名为“变压器模式”,可以直观地反映出该模式在接口转换方面的作用,使得理解和记忆都更加方便。

综上所述,适配器模式是一种非常有用的设计模式,它能够在不修改现有类的基础上,实现接口的转换和适配,从而提高代码的复用性和灵活性。然而,在使用适配器模式时,也需要注意其可能带来的复杂度和维护成本问题。

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

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

相关文章

笔记:在WPF中InvalidateMeasure,InvalidateArrange,InvalidateVisual,UpdateLayout主要功能

一、目的:简要介绍在WPF中InvalidateMeasure,InvalidateArrange,InvalidateVisual,UpdateLayout主要功能 在 WPF 中,InvalidateMeasure、InvalidateArrange、InvalidateVisual 和 UpdateLayout 是用于控制布局系统的四…

Transformer入门(6)Transformer编码器的前馈网络、加法和归一化模块

文章目录 7.前馈网络8.加法和归一化组件9.组合所有编码器组件构成完整编码器 7.前馈网络 编码器块中的前馈网络子层如下图所示: 图1.32 – 编码器块 前馈网络由两个带有ReLU激活函数的全连接层组成。全连接层(Fully Connected Layer)有时也…

OpenWebUI连接不上Ollama模型,Ubuntu24.04

这里写自定义目录标题 问题介绍解决方法 问题介绍 操作系统 Ubuntu24.04Ollama 使用默认安装方法(官网https://github.com/ollama/ollama) curl -fsSL https://ollama.com/install.sh | sh 安装在本机OpenWebUI 使用默认docker安装方法(官网…

前端(async 和await)

1 async async 将 function 变为成为 async 函数 ●async 内部可以使用 await,也可以不使用,因此执行这个函数时,可以使用 then 和 catch 方法 ●async 函数的返回值是一个 Promise 对象 ●Promise 对象的结果由 async 函数执行的返回值决…

Java-25 深入浅出 Spring - 实现简易Ioc-01 Servlet介绍 基本代码编写

点一下关注吧!!!非常感谢!!持续更新!!! 大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了: MyBatis&#xff…

H.323音视频协议

概述 H.323是国际电信联盟(ITU)的一个标准协议栈,该协议栈是一个有机的整体,根据功能可以将其分为四类协议,也就是说该协议从系统的总体框架(H.323)、视频编解码(H.263)、…

WPF+MVVM案例实战与特效(四十)- 一个动态流水边框的实现

文章目录 1、运行效果2、案例实现1、PointAnimationUsingKeyFrames 关键帧动画2、矩形流水边框案例2、运行效果3、关键技术点3、案例拓展:其他形状实现1、圆形流水边框2、心形流水边3、完整页面代码4、运行效果5、总结1、运行效果 2、案例实现 1、PointAnimationUsingKeyFram…

微信小程序--创建一个日历组件

微信小程序–创建一个日历组件 可以创建一个日历组件&#xff0c;来展示当前月份的日期&#xff0c;并支持切换月份的功能。 一、目录结构 /pages/calendarcalendar.wxmlcalendar.scsscalendar.jscalendar.json二、calendar.wxml <view class"calendar"><…

【Linux-ubuntu通过USB传输程序点亮LED灯】

Linux-ubuntu通过USB传输程序点亮LED灯 一,初始化GPIO配置1.使能时钟2.其他寄存器配置 二&#xff0c;程序编译三&#xff0c;USB传输程序 一,初始化GPIO配置 1.使能时钟 使能就是一个控制信号&#xff0c;用于决定时钟信号是否能够有效的传递或者被使用&#xff0c;就像一个…

Rust之抽空学习系列(三)—— 编程通用概念(中)

Rust之抽空学习系列&#xff08;三&#xff09;—— 编程通用概念&#xff08;中&#xff09; 1、变量&可变性 在Rust中&#xff0c;变量默认是不可变的 fn main() {let x 5;println!("x is {}", x); }使用let来声明一个变量&#xff0c;此时变量默认是不可变…

Spring Boot应用开发深度解析与实战案例

Spring Boot应用开发深度解析与实战案例 在当今快速发展的软件开发领域,Spring Boot凭借其“约定优于配置”的理念,极大地简化了Java应用的开发、配置和部署过程,成为了微服务架构下不可或缺的技术选型。本文将深入探讨Spring Boot的核心特性、最佳实践,并通过一个具体的…

Mybatis---事务

目录 引入 一、事务存在的意义 1.事务是什么&#xff1f; 2.Mybatis关于事务的管理 程序员自己控制处理的提交和回滚 引入 一、事务存在的意义 1.事务是什么&#xff1f; 多个操作同时进行,那么同时成功&#xff0c;那么同时失败。这就是事务。 事务有四个特性&#xf…

ModbusTcp获取数据

ModbusTcp获取数据 记录一个用 pymodbus 库来获取数据的代码。 注意&#xff1a; 1.读取寄存器地址是16进制的。2.大小端转换通过代码知道原理。读取数据时&#xff0c;切记频率别太高&#xff0c;否则会出现连接被关闭问题。 from pymodbus.client.sync import ModbusTcpCli…

<项目代码>YOLOv8 车牌识别<目标检测>

项目代码下载链接 &#xff1c;项目代码&#xff1e;YOLOv8 车牌识别&#xff1c;目标检测&#xff1e;https://download.csdn.net/download/qq_53332949/90121387YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题…

闭包:JavaScript 编程中的隐形魔法

在JavaScript中&#xff0c;闭包是一种强大的特性&#xff0c;它允许函数访问其词法作用域外的变量。这种特性使得我们可以创建私有变量和持久化状态&#xff0c;从而编写出更加灵活和强大的代码。本文将深入探讨闭包的定义、原理以及在实际项目中的最佳实践&#xff0c;帮助大…

跨平台开发技术的探索:从 JavaScript 到 Flutter

随着多平台支持和用户体验一致性在应用程序开发中变得越来越重要,开发者面临的挑战是如何在不同平台上保持代码的可维护性和高效性。本文将探讨如何利用现代技术栈,包括 Flutter、JavaScript、HTML5、WebAssembly、TypeScript 和 Svelte,在统一的平台上进行高效的跨平台开发…

华为eNSP:VRRP

一、VRRP背景概述 在现代网络环境中&#xff0c;主机通常通过默认网关进行网络通信。当默认网关出现故障时&#xff0c;网络通信会中断&#xff0c;影响业务连续性和稳定性。为了提高网络的可靠性和冗余性&#xff0c;采用虚拟路由冗余协议&#xff08;VRRP&#xff09;是一种…

linux 内核传参 module_param_cb

简介 https://www.google.com.hk/url?sat&rctj&q&esrcs&sourceweb&cd&ved2ahUKEwiU1Kiz5aOKAxW0wTgGHZjnDL8QFnoECBYQAQ&urlhttps%3A%2F%2Fblog.csdn.net%2Fzongzidedandan%2Farticle%2Fdetails%2F132475615&usgAOvVaw0CR6uvFlW4mqDItx560AH…

虚幻引擎Actor类生命周期

AActor构造函数 在AActor类的构造函数中,虚幻引擎会初始化与该Actor相关的一些关键属性,比如: 默认的组件(如RootComponent、MeshComponent等)。默认的属性设置,例如位置、旋转、缩放等。还会调用BeginPlay等生命周期函数,但在构造函数中,这些函数不会执行。当你在场景…

Referer头部在网站反爬虫技术中的运用

网站数据的安全性和完整性至关重要。爬虫技术&#xff0c;虽然在数据收集和分析中发挥着重要作用&#xff0c;但也给网站管理员带来了挑战。为了保护网站数据不被恶意爬取&#xff0c;反爬虫技术应运而生。本文将探讨HTTP头部中的Referer字段在反爬虫技术中的应用&#xff0c;并…