万字解析设计模式之 适配器模式

一、 适配器模式

1.1概述

将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。

适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

适配器模式的实现有两种方式: 类适配器:一次最多只能适配一个适配者类,不能同时适配多个适配者;适配者类不能为最终类;目标抽象类只能为接口,不能为类。 对象适配器:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类;在适配器中置换适配者类的某些方法比较麻烦。

 1.2结构

适配器模式(Adapter)包含以下主要角色:

  • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  • 适配者(Adaptee)类:实现目标接口,并将不兼容的接口转换为目标接口。它是被访问和适配的现存组件库中的组件接口。
  • 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

1.3 类适配器模式

实现方式:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。

【例】读卡器

现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。

类图如下:

目标(Target)接口

package com.yanyu.Adapter1;


//SD卡的接口
public interface SDCard {
    //读取SD卡方法
    String readSD();
    //写入SD卡功能
    void writeSD(String msg);
}
package com.yanyu.Adapter1;

//SD卡实现类
public class SDCardImpl implements SDCard {
    public String readSD() {
        String msg = "sd card read a msg :hello word SD";
        return msg;
    }
    public void writeSD(String msg) {
        System.out.println("sd card write msg : " + msg);
    }
}

适配者(Adaptee)类:

package com.yanyu.Adapter1;

//TF卡接口
public interface TFCard {
    //读取TF卡方法
    String readTF();
    //写入TF卡功能
    void writeTF(String msg);
}
package com.yanyu.Adapter1;

//TF卡实现类
public class TFCardImpl implements TFCard {
    public String readTF() {
        String msg ="tf card read msg : hello word tf card";
        return msg;
    }
    public void writeTF(String msg) {
        System.out.println("tf card write a msg : " + msg);
    }
}

适配器(Adapter)类

package com.yanyu.Adapter1;

//定义适配器类(SD兼容TF)
public class SDAdapterTF extends TFCardImpl implements SDCard {
    //继承了TFCardImpl类,实现了TF卡的读写功能,并且实现了SDCard接口,使其兼容SD卡

    //实现SD卡的读取方法
    public String readSD() {
        System.out.println("adapter read tf card "); //打印提示信息
        return readTF(); //调用TF卡的读取方法
    }

    //实现SD卡的写入方法
    public void writeSD(String msg) {
        System.out.println("adapter write tf card"); //打印提示信息
        writeTF(msg); //调用TF卡的写入方法
    }
}

客户端类

package com.yanyu.Adapter1;

//电脑类
public class Computer {
    public String readSD(SDCard sdCard) {
        if(sdCard == null) {
            throw new NullPointerException("sd card null");
        }
        return sdCard.readSD();
    }
}
package com.yanyu.Adapter1;

//测试类
public class Client {
    public static void main(String[] args) {
        Computer computer = new Computer(); //创建计算机对象
        SDCard sdCard = new SDCardImpl(); //创建SD卡对象
        System.out.println(computer.readSD(sdCard)); //在计算机上读取SD卡的内容并打印
        System.out.println("------------");

        SDAdapterTF adapter = new SDAdapterTF(); //创建SD适配器对象
        System.out.println(computer.readSD(adapter)); //在计算机上使用适配器读取TF卡的内容并打印
    }
}

类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用。

1.4对象适配器模式

实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。

【例】读卡器

我们使用对象适配器模式将读卡器的案例进行改写。类图如下:

代码如下:

类适配器模式的代码,我们只需要修改适配器类(SDAdapterTF)和测试类。

package com.yanyu.Adapter1;

//定义适配器类(SD兼容TF)

//创建适配器对象(SD兼容TF)
public class SDAdapterTF  implements SDCard {
    private TFCard tfCard;
    public SDAdapterTF(TFCard tfCard) {
        this.tfCard = tfCard;
    }
    public String readSD() {
        System.out.println("adapter read tf card ");
        return tfCard.readTF();
    }
    public void writeSD(String msg) {
        System.out.println("adapter write tf card");
        tfCard.writeTF(msg);
    }
}
package com.yanyu.Adapter1;

//测试类
public class Client {
    public static void main(String[] args) {
        Computer computer = new Computer(); //创建计算机对象
        SDCard sdCard = new SDCardImpl(); //创建SD卡对象
        System.out.println(computer.readSD(sdCard)); //在计算机上读取SD卡的内容并打印
        System.out.println("------------");

        TFCard tfCard = new TFCardImpl();
        SDAdapterTF adapter = new SDAdapterTF(tfCard); //创建SD适配器对象
        System.out.println(computer.readSD(adapter)); //在计算机上使用适配器读取TF卡的内容并打印
    }
}

注意:还有一个适配器模式是接口适配器模式。当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法。而此时我们只需要继承该抽象类即可。

1.5 应用场景

适应的场景:

  • 系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码;
  • 创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。
  • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
  • 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。

1.6JDK源码解析

在JDK中,有很多使用了适配器模式的地方,其中最常见的是集合框架中的迭代器。迭代器是一种提供对集合元素进行访问的机制,它通常被用于循环遍历集合中的元素。在集合框架中,每个集合都提供了一个迭代器,使得可以使用通用的方式对其进行遍历。

另一个使用适配器模式的例子是JDBC(Java DataBase Connectivity)。JDBC是一种用于连接数据库的API,它提供了一系列的接口。其中,Connection、Statement和ResultSet是最常用的接口。JDBC还提供了一种叫做DriverManager的类,它提供了一个用于建立数据库连接的静态方法getConnection()。在JDBC中,不同的数据库供应商会提供不同的驱动程序,这些驱动程序都实现了JDBC的接口。JDBC驱动程序通常被封装在一个适配器中,使得它们可以与JDBC API协同工作。

二、实验

任务描述

现有一个接口 DataOperation 定义了排序方法 sort(int[]) 和查找方法 search(int[],int),已知类 QuickSort 的 quickSort(int[]) 方法实现了快速排序算法,类 BinarySearch 的 binarySearch(int[],int) 方法实现了二分查找算法。

本关任务:现使用适配器模式设计一个系统,在不修改源代码的情况下将类 QuickSort 和类 BinarySearch 的方法适配到 DataOperation 接口中。

,

实现方式

  1. 确保至少有两个类的接口不兼容:一个无法修改 (通常是第三方、 遗留系统或者存在众多已有依赖的类) 的功能性服务类。一个或多个将受益于使用服务类的客户端类。
  2. 声明客户端接口, 描述客户端如何与服务交互。
  3. 创建遵循客户端接口的适配器类。 所有方法暂时都为空。
  4. 在适配器类中添加一个成员变量用于保存对于服务对象的引用。 通常情况下会通过构造函数对该成员变量进行初始化, 但有时在调用其方法时将该变量传递给适配器会更方便。
  5. 依次实现适配器类客户端接口的所有方法。 适配器会将实际工作委派给服务对象, 自身只负责接口或数据格式的转换。
  6. 客户端必须通过客户端接口使用适配器。 这样一来, 你就可以在不影响客户端代码的情况下修改或扩展适配器。

编程要求

根据提示,在右侧编辑器 Begin-End 内补充 "OperationAdapter.java" 的代码,计算并输出结果。其它文件不需要修改。

测试说明

平台会对你编写的代码进行测试:第一行输入数组个数,第二行输入数组元素,第三行输出需要查询的数。查询的结果 1 表示“找到了”,-1 表示“没有找到”

测试输入: 741 2 58 12 66 98 512; 预期输出: 实现快速排序: 2 5 12 41 58 66 98 实现了二分查找算法: 1

测试输入: 858 40 12 66 77 5 48 2310; 预期输出: 实现快速排序: 5 12 23 40 48 58 66 77 实现了二分查找算法: -1

目标接口

package step1;

public interface DataOperation {
    public void sort(int array[]);
    public int search(int array[],int key);
}

DataOperation接口定义了客户端代码所期望的操作,即sort和search方法,而适配器将快速排序和二分查找算法适配到这个接口中,使得客户端可以统一调用这两种算法。

适配者(Adaptee)类

package step1;

public class BinarySearch {
    public int binarySearch(int array[],int key)
    {
        int low = 0;
        int high = array.length -1;
        while(low <= high)
        {
            int mid = (low + high) / 2;
            int midVal = array[mid];

            if(midVal < key)
            {
                low = mid +1;
            }
            else if(midVal > key)
            {
                high = mid -1;
            }
            else
            {
                return 1; //找到元素返回1
            }
        }
        return -1;  //未找到元素返回-1
    }

}
package step1;

public class QuickSort {
    public int[] quickSort(int array[])
    {
        sort(array,0,array.length-1);
        return array;
    }
    public void sort(int array[],int p, int r)
    {
        int q=0;
        if(p<r)
        {
            q=partition(array,p,r);
            sort(array,p,q-1);
            sort(array,q+1,r);
        }
    }
    public int partition(int[] a, int p, int r)
    {
        int x=a[r];
        int j=p-1;
        for(int i=p;i<=r-1;i++)
        {
            if(a[i]<=x)
            {
                j++;
                swap(a,j,i);
            }
        }
        swap(a,j+1,r);
        return j+1;
    }
    public void swap(int[] a, int i, int j)
    {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

适配器(Adapter)类

package step1;

/********** Begin *********/
// OperationAdapter类实现了DataOperation接口,将QuickSort和BinarySearch适配到DataOperation接口中

public class OperationAdapter implements DataOperation{
    private QuickSort qSort; // 适配者1:快速排序算法
    private BinarySearch binarySearch; // 适配者2:二分查找算法

    // 构造方法,接收快速排序和二分查找算法对象
    public OperationAdapter(QuickSort qSort, BinarySearch binarySearch){
        this.qSort = qSort;
        this.binarySearch = binarySearch;
    }

    // 实现DataOperation接口中的sort方法,调用适配者1的快速排序算法
    public void sort(int[] array){
        qSort.quickSort(array);
    }

    // 实现DataOperation接口中的search方法,调用适配者2的二分查找算法
    public int search(int[] array, int key){
        return binarySearch.binarySearch(array, key);
    }
}
/********** End *********/

客户端类

package step1;

import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        // 创建适配者模式的适配器对象,将快速排序和二分查找算法适配到统一的接口
        DataOperation dataOperation = new OperationAdapter(new QuickSort(),new BinarySearch());
        int i = 0;
        Scanner scanner = new Scanner(System.in);
        int count = scanner.nextInt();
        int[] array = new int[count];
        // 读取输入的数组元素
        while (scanner.hasNext()) {
            array[i++] = scanner.nextInt();
            if (i == array.length) {
                break;
            }
        }
        int key = scanner.nextInt();
        // 使用适配者模式调用快速排序算法
        dataOperation.sort(array);
        System.out.println("实现快速排序:");
        // 输出排序后的数组
        for(i = 0; i<array.length; i++){
            System.out.print(array[i]+" ");
        }
        System.out.println("\n"+"实现了二分查找算法:");
        // 使用适配者模式调用二分查找算法
        System.out.println(dataOperation.search(array, key));
    }
}

 

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

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

相关文章

imx VPU解码分析4-wrap与hantro的关系

前面已经分析了wrap和hantro&#xff0c;但是二者是如何结合的&#xff0c;wrap是如何封装hantro的&#xff0c;提供了哪些接口&#xff0c;封装了哪些细节还不太清楚&#xff0c;此文来探究下。这里还是只关注解码。 imx VPU解码分析1-wrap-CSDN博客 imx VPU解码分析2-hantr…

值得收藏推荐的 21 款免费数据恢复软件工具

使用这些免费数据恢复工具 之一找回您认为永远消失的文件。我根据这些程序的易用性和提供的功能对这些程序进行了排名。 这些应用程序从您的硬盘驱动器、USB 驱动器、媒体卡等恢复文档、视频、图像、音乐等。我建议每个计算机所有者安装其中一个程序&#xff0c;最好尽快&#…

【MySQL】一些内置函数(时间函数、字符串函数、数学函数等,学会了有妙用)

内置函数 前言正式开始时间函数显示当前日期、时间、日期时间的日期计算相差多少天示例创建一张表&#xff0c;记录生日 留言表 字符串函数charsetconcatinstr(string, substring)ucase和lcaseleft(string, length)length求字符串长度replace(str, search_str, replace_str)tri…

【LeetCode刷题笔记】DFSBFS(一)

51. N 皇后 解题思路: DFS + 回溯 :由于 NxN 个格子放 N 个皇后, 同一行不能放置 2 个皇后,所以皇后必然放置在不同行 。 因此,可以从第 0 行开始,逐行地尝试,在每一个 i

Pyside6/PyQt6的QTreeWidget如何添加多级子项,如何实现选中父项,子项也全部选中功能,源码示例

文章目录 📖 介绍 📖🏡 环境 🏡📒 使用方法 📒📝 数据📝 源码📖 介绍 📖 在UI开发中经常会需要展示/让用户多层级选择,这篇文章记录了一个QTreeWidget如何添加多级子项,如何实现选中父项,子项也全部选中/取消选中功能的源码示例,大家可以举一反三实现自…

合理运用ChatGPT使用Python编写一个桌面便签应用

ChatGPT的编程能力也不差&#xff0c;本次我就一步一步提要求&#xff0c;让ChatGPT根据我的要求&#xff0c;编写出一个可用的&#xff0c;可打包运行的桌面便签。 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QAction, QSystemTrayIco…

php一句话木马免杀

php一句话木马免杀 针对于php一句话木马做免杀&#xff1a; 利用php动态函数的特性&#xff0c;将危险函数拆分成字符&#xff0c;最终使用字符串拼接的方式&#xff0c;然后重新拼接&#xff0c;后加括号执行代码&#xff0c;并且可以使用花指令进行包装&#xff0c;如无限i…

Unity收费对谁影响最大

Unity的收费政策对以下几类人群影响最大&#xff1a; 游戏开发商&#xff1a;Unity收费政策中最直接的影响对象就是游戏开发商。对于那些使用Unity引擎制作游戏的开发商来说&#xff0c;他们将需要考虑新的许可证费用和服务费用&#xff0c;这可能会对他们的盈利和发展产生影响…

springboot项目基于jdk17、分布式事务seata-server-1.7.1、分库分表shardingSphere5.2.1开发过程中出现的问题

由于项目需要&#xff0c;springboot项目需基于jdk17环境开发&#xff0c;结合nacos2.0.3、分布式事务seata-server-1.7.1、分库分表shardingSphere5.2.1等&#xff0c;项目启动过程中出现的问题解决方式小结。 问题一&#xff1a; Caused by: java.lang.RuntimeException: j…

C++ LibCurl实现Web指纹识别

Web指纹识别是一种通过分析Web应用程序的特征和元数据&#xff0c;以确定应用程序所使用的技术栈和配置的技术。这项技术旨在识别Web服务器、Web应用框架、后端数据库、JavaScript库等组件的版本和配置信息。通过分析HTTP响应头、HTML源代码、JavaScript代码、CSS文件等&#x…

【Mysql系列】LAG与LEAD开窗函数

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

visionOS空间计算实战开发教程Day 5 纹理和材质

在​​Day 4​​​中我们使用了​​ImmersiveSpace​​并在其中添加了一个立方体&#xff0c;但对这个立方体我们只配置了长宽高&#xff0c;并没有做进一步的操作。 本文中我们会通过纹理和材质对这个立方体的六个面分别进行不同的绘制。首先我们将​​ImmersiveView​​分拆…

Redis入门与应用

目录 Redis的技术全景 两大维度 三大主线 Redis的版本选择与安装 Redis的linux安装 Redis的启动 默认配置 带参数启动 配置文件启动 操作 停止 Redis全局命令 键名的生产实践 Redis常用数据结构 字符串&#xff08;String&#xff09; 操作命令 set 设置值 g…

XDAG同步节点部署

系统环境要求 JDK : v17 Maven : v3.9.1-v3.9.5 MySQL : v8.0系列 1、MySQL8.0安装 1&#xff09;docker-compose安装详情 MySQL安装 2&#xff09;配置数据库账号密码及键表 # docker exec -it mysql8 /bin/bash # root0286a1fd60e6:/# mysql -uroot -p Enter password:…

Android : ListView + BaseAdapter-简单应用

​​容器与适配器&#xff1a;​​​​​ http://t.csdnimg.cn/ZfAJ7 示例图&#xff1a; 实体类 News.java package com.example.mylistviewbaseadapter.entity;public class News {private String title;private String content;private int img;public News(Str…

Django报错:RuntimeError at /home/ 解决办法

错误提示&#xff1a; RuntimeError at /home/ Model class django.contrib.contenttypes.models.ContentType doesnt declare an explicit app_label and isnt in an application in INSTALLED_APPS. 原因剖析&#xff1a; 博主在使用pycharm创建Django项目的时候&#xff0…

golang 断点调试

1.碰见如下报错,调试器没有打印变量信息 Delve is too old for Go version 1.21.2 (maximum supported version 1.19) 2. 解决办法 升级delve delve是go语言的debug工具。 go install github.com/go-delve/delve/cmd/dlvlatest报错 Get “https://proxy.golang.org/github…

第四代智能井盖传感器:智能井盖监测传感器怎么监测井盖位移

大街小巷的井盖是城市基础设施的重要组成部分&#xff0c;关系到广大市民的生活质量与安全。政府部门始终将其列为重要的建设和管理对象&#xff0c;通过高效的管理和维护&#xff0c;可以增强市民的安全感和幸福感。然而单纯依赖人工检修的方式&#xff0c;无疑会使工作量和工…

31、Flink的SQL Gateway介绍及示例

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

【opencv】计算机视觉:停车场车位实时识别

目录 目标 整体流程 背景 详细讲解 目标 我们想要在一个实时的停车场监控视频中&#xff0c;看看要有多少个车以及有多少个空缺车位。然后我们可以标记空的&#xff0c;然后来车之后&#xff0c;实时告诉应该停在那里最方便、最近&#xff01;&#xff01;&#xff01;实现…