细说适配器模式

结构型模式,顾名思义讨论的是类和对象的结构,它采用继承机制来组合接口或实现,或者通过组合一些对象,从而实现新的功能。GoF23种设计模式中的结构型模式有7种,分别是适配器模式(Adapter)、装饰器模式(Decorator)、代理模式(Proxy)、外观模式(Facade)、桥接模式(Bridge)、组合模式(Composite)、享元模式(Flyweight)。

其中对象的适配器模式是各种模式的起源,是一种比较重要的适配器模式。这7种模式的作用不同,分别如下。
    适配器模式(Adapter):将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。
    根据适配对象的不同,又分为3种类型:
       类的适配器模式:用于对类进行适配。
       对象的适配器模式:用于对对象进行包装。
       接口的适配器模式:用于对接口抽象化。
    装饰器模式(Decorator):向某个对象动态地添加更多的功能。装饰器模式是除类继承外另一种扩展功能的方法。
    代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问。
    外观模式(Facade):为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
    桥接模式(Bridge):将一个抽象与实现解耦,以便两者可以独立的变化。
    组合模式(Composite):把多个对象组成树状结构来表示局部与整体,这样用户可以一样地对待单个对象和对象的组合。
    享元模式(Flyweight):通过共享以便有效地支持大量小颗粒对象。

接下来将对7种常用结构型模式进行比较,并通过经典的代表实例来说明该如何使用各种结构型模式:
    适配器模式—Iterator适配器(对象的适配器模式)、Enumeration适配器(对象的适配器模式)、AWT事件适配器(接口的适配器模式)、I/O字节流到字符流的适配器(对象的适配器模式)。
    装饰器模式—I/O输入/输出流管道的装饰器模式、Sitemesh装饰器。
    代理模式—Java动态代理机制。
    桥接模式—JDBC桥DriverManager。
    组合模式—AWT容器Container。
    享元模式—数据库连接池。

--------------------------------------------------------------------------------------------------------------------------
下面来看详细的内容。

适配器模式(Adapter)
下面从以下几个方面来详细讲解适配器模式。
    适配器模式的核心思想。
    第一种:类的适配器模式(对类进行适配)。
    第二种:对象的适配器模式(对对象进行包装)。
    第三种:接口的适配器模式(对接口抽象化)。
    何时使用适配器模式。
    Java中的应用—Iterator适配器(对象的适配器模式)。
    Java中的应用—Enumeration适配器(对象的适配器模式)。
    Java中的应用—AWT事件适配器(接口的适配器模式)。
    Java中的应用—I/O字节流到字符流的适配器(对象的适配器模式)。

适配器模式的核心思想
适配器模式的核心思想:把原有的接口转变成调用者期待的接口,从而使不同接口的类可以一起工作。
适配器中包含如下3个角色。
    源角色Adaptee:需要适配的目标类或接口。
    目标角色Target:所期望得到的接口。
    适配器角色Adapter:适配器类是本模式的核心,用来把源接口转换成目标接口,显然这一角色不可以是接口,而必须是具体类。
这3者角色之间的交互关系便组成了适配器模式的模型,
Adaptee类只有operation()方法,没有newoperation()方法,但是客户端又需要目标类同时拥有这两个方法,这时便可以新建一个接口Target,并提供一个中间环节Adapter类,Adapter类实现了Target接口,并继承自Adaptee,Adapter类的operation()方法重新封装了Adapter类的operation()方法,并同时实现了newoperation()方法,这便实现了适配的目的。

 
适配器也叫做包装器模式(Wrapper),根据适配对象的不同,又可以将适配器模式分为3种子类型。
    类的适配器模式:用于对类进行适配。
    对象的适配器模式:用于对对象进行包装。
    接口的适配器模式:用于对接口抽象化。

下面分别通过实例的形式来展示这3种模式的使用。

第一种:类的适配器模式(对类进行适配)
第一种模式是类的适配器模式,它用来对目标类进行包装,如图12-3所示。
    Source类是具体的原始类,是待适配的对象,它拥有一个函数operation1()。
    Targetable是要适配的目标接口,它拥有一个与Source同样的接口函数operation1(),并提供了一个新的接口函数operation2(),该函数是要扩展的功能。
    Adapter是适配器类,它必须继承Source类,并实现Targetable接口,从而将Source的功能扩展到Targetable接口所具有的功能。
适配后的Source类,即可以通过调用Targetable接口来实现对Source类的操作,

下面来看具体的实现。
(1)Source类的源代码如程序12-1所示,其默认的操作函数operation1()用来输出一个字符串。
源类Source.java

package structure.adapter;public class Source {public void operation1() {System.out.println("原始类的方法");}
}


(2)Targetable必须首先具备与Source相同的函数接口,这样它才能够实现Source功能,然后增加扩展函数operation2()。
目标接口Targetable.java

package structure.adapter;public interface Targetable {public void operation1();public void operation2();
}


(3)Adapter的作用是将Source适配为Targetable,因此它必须继承Source类并实现Targetable接口,这样它便拥有了Source函数operation1()的功能,该函数还拥有了与Targetable同样的接口;同时,它必须实现Targetable的扩展函数,它往控制台输出了一个字符串。
适配器类Adapter.java

package structure.adapter;public class Adapter extends Source implements Targetable {public void operation2() {System.out.println("适配目标类后的方法");}
}


(4)我们就可以创建一个Adapter类的对象,该对象属于Targetable,调用该对象的operation1()函数将会实现对Source函数的调用,调用operation2()函数将会实现对Adatper实现函数的调用,这样就实现了对类Source到Targetable的适配。
测试类AdapterTest.java

package structure.adapter;public class AdapterTest {public static void main(String[] args) {// 创建目标接口的类实例Targetable obj = new Adapter();// 调用目标类的方法obj.operation1();obj.operation2();}
}


运行该程序的输出为:
原始类的方法
适配目标类后的方法
这正是我们想要的结果:把类Source按照接口Targetable进行调用。

第二种:对象的适配器模式(对对象进行包装)
第二种模式是对象的适配器模式,它用来对目标对象进行包装,因此又叫做包装器模式,如图12-4所示。
    Source类是具体的原始类,是待包装对象的类,它拥有一个函数operation1()。
    Targetable是要适配的目标接口,它拥有一个与Source同样的接口函数operation1(),并提供了一个新的接口函数operation2(),该函数是要扩展的功能。
    Wrapper是包装器类,它与Adapter适配器不同,它不需要继承Source,但必须拥有一个Source类型的对象source,该对象在构造函数中被赋值。在该包装器的operation1()函数中,需要调用source的operation1(),这样就实现了对原有对象的调用;同时扩展实现Targetable的operation2()函数。
包装后的Wrapper类,即可以通过调用Targetable接口来实现对Source类的操作,以上的Source类和Targetable接口与第一种模式相同,唯一不同的是Wrapper的实现方式不同。
包装器类Wrapper.java

package structure.adapter;public class Wrapper implements Targetable {private Source source;public Wrapper(Source source) {super();this.source = source;}public void operation1() {source.operation1();}public void operation2() {System.out.println("包装目标类后的方法");}
}


创建一个Source类的对象source,并根据该对象创建一个Wrapper包装器obj,该包装器对象属于Targetable,调用该对象的operation1()函数将会实现对Source函数的调用,调用operation2()函数将会实现对Wrapper实现函数的调用,这样就实现了对类Source到Targetable的包装。
测试类WrapperTest.java

package structure.adapter;public class WrapperTest {public static void main(String[] args) {// 创建源类对象Source source = new Source();// 创建source的包装类对象Targetable obj = new Wrapper(source);// 调用目标类的方法obj.operation1();obj.operation2();}
}


运行该程序的输出为:
原始类的方法
包装目标类后的方法
输出的结果与第一种模式相同,因此效果是与第一种模式相同的,不同的是适配的方式不同。

第三种:接口的适配器模式(对接口抽象化)
有时我们会在一个接口中定义多个接口方法,如果要实现该接口编写一个类,就必须为每一个接口方法编写实现代码,这显然会造成很大的浪费。为了解决这个问题,可以使用第三种适配器模式—默认适配器。它会为原有的接口类实现一个默认的抽象类,在该抽象类中编写每一个接口的默认实现,当我们需要编写一个具体类时,只需要继承自该抽象类,而不需要实现原有的接口。并且,此时我们不需要实现所有的接口方法,只实现需要的函数即可。
如图12-5所示为接口的适配器模式。

    Sourceable是定义了多个接口函数的接口类。
    DefaultWrapper是一个抽象类,它实现了接口Sourcable,并为每一个接口函数提供了默认的实现。
依据DefaultWrapper就可以编写不同的实现,在实现中只需要重写部分待实现的函数,而不需要重写全部。
 
下面来看具体的实现。
(1)Sourcable类的源代码如程序12-7所示,它定义了两个操作函数。
源接口Sourcable.java

package structure.adapter;public interface Sourcable {public void operation1();public void operation2();
}


(2)DefaultWrapper实现了Sourcable接口,并提供了其两个接口函数的实现,在该实现中可以什么也不做,目的只是为了给其子类提供一个默认的实现。
默认适配器类DefaultWrapper.java

package structure.adapter;public abstract class DefaultWrapper implements Sourcable {public void operation1(){}public void operation2(){}
}


(3)SourceSub1继承自DefaultWrapper,由于DefaultWrapper的屏蔽作用,SourceSub1可以只重新实现自己关心的函数operation1(),它负责输出一个字符串。
源接口的实现子类SourceSub1.java

package structure.adapter;public class SourceSub1 extends DefaultWrapper {public void operation1() {System.out.println("源接口的一个实现子类Sub1");}
}


(4)SourceSub2继承自DefaultWrapper,由于DefaultWrapper的屏蔽作用,SourceSub2可以只重新实现自己关心的函数operation2(),它负责输出一个字符串。
源接口的实现子类SourceSub2.java

package structure.adapter;public class SourceSub2 extends DefaultWrapper {public void operation2() {System.out.println("源接口的一个实现子类Sub2");}
}


以上我们编写了两个实现SourceSub1和SourceSub2,下面分别创建一个对象,创建的对象都属于Sourcable。然后分别调用它们的方法operation1()和operation2()。
测试类DefaultWrapperTest.java

package structure.adapter;
public class DefaultWrapperTest {public static void main(String[] args) {Sourcable source1 = new SourceSub1();Sourcable source2 = new SourceSub2();source1.operation1();source1.operation2();source2.operation1();source2.operation2();}
}


运行该程序的结果如下:
源接口的一个实现子类Sub1
源接口的一个实现子类Sub2
从输出的结果可以看出,source1和source2仅仅在运行自身实现的函数时发生了作用,对于没有实现的函数则调用了默认适配器DefaultWrapper的默认函数,什么也没有输出。

何时使用适配器模式
从以上的讲解我们已经知道,如果需要将一个类变成另一个类时就可以使用适配器模式。但是根据需求的不同,可以分别选用3种不同的子模式。
    类的适配器模式:当希望将一个类转换成满足另一个接口时,可以模仿Adapter的做法来构造一个新的适配器类,该类继承原有的类并实现新的接口即可。
    对象的适配器模式:当希望将一个对象转换成另一个接口时,可以模仿Wrapper的做法来构造一个新的包装类,该类调用原有的类并实现新的接口即可。
    默认适配器模式:当不希望实现一个接口的所有方法时,可以模仿DefaultWrapper的做法构造一个抽象类,给出所有方法的默认的实现,这样,从这个抽象类再继承下去的子类就不必实现所有的方法了。

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

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

相关文章

进阶JS-内置构造函数

基本数据类型:string、number、boolean、undefined、null 引用类型:对象 其实字符串、数值、布尔等基本类型也都有专门的构造函数,这些我们称为包装类型。 JS中几乎所有的数据都可以基于构成函数创建。 const str andy//其实是const strnew String(a…

在windows的ubuntu LTS中安装及使用EZ-InSAR进行InSAR数据处理

EZ-InSAR(曾被称为MIESAR,即Matlab界面用于易于使用的合成孔径雷达干涉测量)是一个用MATLAB编写的工具箱,用于通过易于使用的图形用户界面(GUI)进行干涉合成孔径雷达(InSAR)数据处理…

开源博客项目Blog .NET Core源码学习(3:数据库操作方式)

开源博客项目Blog采用SqlSugar模块连接并操作数据库,本文学习并记录项目中使用SqlSugar的方式和方法。   首先,数据库连接信息放在了App.Hosting项目的appsettings.json中DbConfig节,支持在DbConfig节配置多个数据库连接信息,以…

基于FFmpeg+SDL的视频播放器的制作

基于FFmpegSDL的视频播放器的制作 基于FFmpegSDL的视频播放器的制作实验1实验2实验3实验4基本练习进阶练习 实验5实验6 基于FFmpegSDL的视频播放器的制作 雷霄骅博士的课程。 课程链接:https://blog.csdn.net/leixiaohua1020/article/details/47068015 初学 FFmp…

uniapp 如何动态切换应用图标、名称

有时候我们需要实现类似百度网盘、淘宝APP这种可以动态切换 但是呢这种需求平常非常少见 很多人不知道如何操作 今天就教大家如何实现 这里我们需要用到一款插件Ba-ChangeIcon Ba-ChangeIcon 是一款uniapp动态切换应用图标、名称的插件。可实现过年、过节动态切换应用图标的效…

MongoDB索引

索引支持在MongoDB中高效执行查询。如果没有索引,MongoDB必须扫描集合中的每个文档才能返回查询结果。如果查询存在适当的索引,MongoDB将使用该索引来限制它必须扫描的文档数。 尽管索引提高了查询性能,但添加索引对写入操作的性能有负面影响…

DataX

序言 弄过了Chunjun 过来搞搞DataX3.0 https://github.com/alibaba/DataXhttps://github.com/alibaba/DataX/blob/master/introduction.mdhttps://github.com/alibaba/DataX/blob/master/userGuid.md 简介 ​ DataX 是一个异构数据源离线同步工具,致力于实现包…

如何使用ArcGIS Pro直接获取道路中心线

以前使用ArcGIS获取道路中心线,需要先将面要素转换为栅格再获取中心线,现在我们可以通过ArcGIS Pro直接获取道路中心线,这里为大家介绍一下获取方法,希望能对你有所帮助。 新建地理数据库 在存储数据的文件夹上点击右键&#xff…

QT--Opencv下报错Mat/imwrite/imread找不到文件

像file not found这类错误 原因是编程系统找不到所指库文件,以此为例,排查自己的每个位置是否有误 1. .pro文件 添加opencv动态库 INCLUDEPATH /usr/include \/usr/include/opencv4 \/usr/include/opencv4/opencv2LIBS /usr/lib/aarch64-linux-gnu…

【C++】STL之list深度剖析及模拟实现

目录 前言 一、list 的使用 1、构造函数 2、迭代器 3、增删查改 4、其他函数使用 二、list 的模拟实现 1、节点的创建 2、push_back 和 push_front 3、普通迭代器 4、const 迭代器 5、增删查改(insert、erase、pop_back、pop_front) 6、构造函数和析构函数 6.1、默认构造…

CSS box-shadow阴影

1、语法 box-shadow: h-shadow v-shadow blur spread color inset; 值描述h-shadow必需的。水平阴影的位置。允许负值v-shadow必需的。垂直阴影的位置。允许负值blur可选。模糊距离spread可选。阴影的大小color可选。阴影的颜色。在CSS颜色值寻找颜色值的完整列表inset可选。…

【Linux】Linux远程访问Windows下的MySQL数据库

1.建立Windows防火墙规则 首先需要开放windows防火墙,针对3306端口单独创建一条规则,允许访问。 打开windows安全中心防火墙与保护,点击高级设置 进入之后,点击入站规则,新建一条规则 新建端口入站规则 端口填写330…

springboot 下载文件为excel数据,中文自定义单元格宽度

/**2 * Description:表格自适应宽度(中文支持)3 * Author: 4 * param sheet sheet5 * param columnLength 列数6 */7 private static void setSizeColumn(HSSFSheet sheet, int columnLength) {8 for (int columnNum 0; columnNum < …

Oracle 查询 SQL 语句

目录 1. Oracle 查询 SQL 语句1.1. 性能查询常用 SQL1.1.1. 查询最慢的 SQL1.1.2. 列出使用频率最高的 5 个查询1.1.3. 消耗磁盘读取最多的 sql top51.1.4. 找出需要大量缓冲读取(逻辑读)操作的查询1.1.5. 查询每天执行慢的 SQL1.1.6. 从 V$SQLAREA 中查询最占用资源的查询1.1.…

使用vue-cli搭建SPA项目

目录 引言 什么是SPA&#xff1f; Vue CLI 是什么&#xff1f; 步骤1&#xff1a;安装 Vue CLI 为什么选择 Vue CLI 作为项目搭建工具 安装vue-cli 步骤2&#xff1a;创建新的 Vue 项目 创建成功后的项目结构 步骤3&#xff1a;项目结构概述 vue项目结构说明 步骤4&a…

Vue中前端导出word文件

很多时候在工作中会碰到完全由前端导出word文件的需求&#xff0c;因此特地记录一下比较常用的几种方式。 一、提供一个word模板 该方法提供一个word模板文件&#xff0c;数据通过参数替换的方式传入word文件中&#xff0c;灵活性较差&#xff0c;适用于简单的文件导出。需要…

论文笔记(整理):轨迹相似度顶会论文中使用的数据集

0 汇总 数据类型数据名称数据处理出租车数据波尔图 原始数据&#xff1a;2013年7月到2014年6月&#xff0c;170万条数据 ICDE 2023 Contrastive Trajectory Similarity Learning with Dual-Feature Attention 过滤位于城市&#xff08;或国家&#xff09;区域之外的轨迹 过…

Idea引入thymeleaf失败解决方法

报错 Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback.Fri Sep 29 09:42:00 CST 2023 There was an unexpected error (typeNot Found, status404). 原因&#xff1a;html没有使用thymeleaf 首先要引入…

Linux Day18 TCP_UDP协议及相关知识

一、网络基础概念 1.1 网络 网络是由若干结点和连接这些结点的链路组成&#xff0c;网络中的结点可以是计算机&#xff0c;交换机、 路由器等设备。 1.2 互联网 把多个网络连接起来就构成了互联网。目前最大的互联网就是因特网。 网络设备有&#xff1a;交换机、路由器、…

【MATLAB源码-第38期】基于OFDM的块状导频和梳状导频误码率性能对比,不同信道估计方法以及不同调制方式对比。

1、算法描述 块状导频和梳状导频都是用于无线通信系统中信道估计的方法。 块状导频&#xff1a; 定义&#xff1a; 在频域上&#xff0c;块状导频是连续放置的一组导频符号。这意味着所有的导频符号都集中在一个短的时间段内发送。 优点&#xff1a; 对于时间选择性信道&#…