【结构型模式】适配器模式

一、适配器模式概述

        适配器模式的定义-意图将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。(对象结构模式->对象适配器/类结构模式->类适配器)

        适配器模式包含三个角色:目标(Target)角色、适配者(Adaptee)角色和适配器(Adapter)角色。根据Adapter角色的实现不同,可分为类适配器和对象适配器

  • 类适配器模式(继承也是泛化实现):适配器角色对于被适配角色的适配是通过继承完成的还是通过组合来完成的。通过继承类适配者类(Adaptee Class)实现的,另外类适配器实现客户类所需要的接口。当客户对象调用适配器类方法的时候,适配器内部调用它所继承的适配者的方法。

  •  对象适配器模式(组合实现):适配器角色对于被适配角色的适配是通过组合来完成的。包含一个适配器者的引用(reference),与类适配器相同,对象适配器也实现了对客户类需要用接口。当客户对象调用对象适配器的方法的时候,对象适配器调它所包含的适配器者实例的适当方法。

  • 适配器模式的优缺点
    • 优点
      • 1.将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构;
      • 2.增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者类可以在多个不同的体统中复用;
      • 3.灵活性和扩展性非常好;
      • 4.类适配器模式:置换一些适配者的发放很方便;
      • 5.对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类。
    • 缺点
      • 1.类适配器模式:
        • ①一次最多只能适配一个适配者类,不能同时适配多个适配者;
        • ②适配者类不能为最终类(不能为final);
        • ③目标抽象类只能为接口,不能为类。
      • 2.对象适配器模式:在适配器中置换适配者类的某些方法比较麻烦。
  • 适用环境
    • 1.系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码;
    • 2.创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。

二、缺省适配器

       缺省适配器模式(Defualt Adapter Pattern)的定义当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为借口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又成为单接口适配器模式

        结构

  • 适配者接口(ServiceInterface)
  • 缺省适配器类(AbstractServiceClass)
  • 具体业务类(ConcreteServiceClass)。

三、双向适配器

        双向适配器定义在对象适配器的使用过程,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过对它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。

        结构

  • 目标(Target)角色
  • 适配者(Adaptee)角色
  • 适配器(Adapter)角色。

四、代码实现

        适配器模式的结构和实现:

  • 1.目标(Target)角色:这是客户所期待的接口。因为Java不支持多继承,所以Target必须是接口,不可以是类。
  • 2.适配者(Adaptee)角色:需要适配的类。
  • 3.适配器(Adapter)角色:把源接口转换成目标接口。
        4.1 对象适配器

        某公司欲开发一款儿童玩具汽车,为了更好地吸引小朋友的注意力,该玩具汽车在移动过程中伴随着灯光闪烁和声音提示。在该公司以往的产品中已经实现了控制灯光闪烁(例如警灯闪烁)和声音提示(例如警笛音效)的程序,为了重用先前的代码并且使得汽车控制软件具有更好的灵活性和扩展性,现使用适配器模式设计该玩具汽车控制软件。

        4.1.1 目标(Target)角色(CarController:汽车控制类,充当目标抽象类)
package Adapter.car;
//汽车控制类,充当目标抽象类
public abstract class CarController {
	public void move() {
		System.out.println("车开始移动!");
	}
	public abstract void phonate();//发出声音
	public abstract void twinkle();//灯光闪烁
}
        4.1.2 适配者(Adaptee)角色(PoliceSound:警笛类,充当适配者;PoliceLamp:警灯类,充当适配者)
package Adapter.car;
//警车声音类,充当适配器
public class PoliceSound {
	public void alarmSound() {
		System.out.println("发出警车声音!");
	}
}
package Adapter.car;
//警车灯类,充当适配者
public class PoliceLamp {
	public void alarmLamp() {
		System.out.println("呈现警车灯闪烁!");
	}
}
        4.2.3 适配器(Adapter)角色(PoliceCarAdapter:警车适配器,充当适配器)
package Adapter.car;
//适配器角色
public class PoliceCarAdapter extends CarController{
	//定义适配者PoliceSound对象
	private PoliceSound sound;
	//定义适配者PoliceLamp对象
	private PoliceLamp lamp;
	public PoliceCarAdapter() {
		sound=new PoliceSound();
		lamp=new PoliceLamp();
	}
	@Override
	public void phonate() {
		// TODO 自动生成的方法存根
		sound.alarmSound();
	}

	@Override
	public void twinkle() {
		// TODO 自动生成的方法存根
		lamp.alarmLamp();
	}

}
        4.2.4 main方法实现对象适配器模式
package Adapter.car;

public class Test {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		CarController cc;//针对抽象目标编程
		//cc =new AmbulanceCarAdapter();//救护车适配器
		//cc.move();
		//cc.twinkle();
		//cc.phonate();
		System.out.println("----------------");
		cc=new PoliceCarAdapter();//警车适配器
		cc.move();
		cc.twinkle();
		cc.phonate();	
	}
}
        4.1.5 UML图

        4.2 缺省适配器

        本科生毕业条件:1.本科生必须读完读四年修满所有的学分后,参加论文答辩后即可毕业;2.学校采用的弹性学分制,只要修满所有的学分,可以提前安排答辩早毕业。

        要求:分别实现以下三种情况的学生类,并编写测试类运行。

  • 1)普通的就读四年的全日制大学生 GeneralStudent
  • 2)读了3年提前修满学分特殊的大学生SpecialStudent
  • 3)读到大二就退学的大学生DiscontinueStudyStudent
        4.2.1 目标(Target)角色(Study:学分接口,充当目标接口)

 

package Adapter.defualtAdapter.Student;
//目标Target角色
public interface Study {
	//四个学期和论文答辩
	public void StudyGrade1();
	public void StudyGrade2();
	public void StudyGrade3();
	public void StudyGrade4();
	public void TheisPresentation();
}
        4.2.2 适配者(Adaptee)角色(GeneralStudent、SpecialStudent、DiscountinueStudyStudent:充当适配者)

 

package Adapter.defualtAdapter.Student;
//Adaptee普通全日制大学生
public class GeneralStudent extends StudyAdapter{
	public GeneralStudent(String name) {
		super(name);
	}
	public void StudyGrade1(){
		System.out.println("读完大学一年级");
	}
	public void StudyGrade2(){
		System.out.println("读完大学二年级");
	}
	public void StudyGrade3(){
		System.out.println("读完大学三年级");
	}
	public void StudyGrade4(){
		System.out.println("读完大学四年级");
	}
	public void TheisPresentation() {
		super.TheisPresentation();
		System.out.println("修满了学分,参加论文答辩,成功毕业!");
	}
}
package Adapter.defualtAdapter.Student;
//Adaptee跳级的大学生
public class SpecialStudent extends StudyAdapter{
	public SpecialStudent(String name) {
		super(name);
	}
	public void StudyGrade1(){
		System.out.println("读完大学一年级");
	}
	public void StudyGrade2(){
		System.out.println("读完大学二年级");
	}
	public void TheisPresentation() {
		super.TheisPresentation();
		System.out.println("修满了学分,参加论文答辩,成功毕业!");
	}
}
package Adapter.defualtAdapter.Student;
//Adaptee辍学的大学生
public class DiscountinueStudyStudent extends StudyAdapter{

	public DiscountinueStudyStudent(String name) {
		super(name);
	}
	public void StudyGrade1(){
		System.out.println("读完大学一年级");
	}
	public void TheisPresentation() {
		super.TheisPresentation();
		System.out.println("中途辍学!");
	}
}
        4.2.3 适配器(Adapter)角色(StudyAdapter:学分适配器,充当适配器)
package Adapter.defualtAdapter.Student;
//适配器Adapter
public abstract class StudyAdapter implements Study{
	String name;
	public StudyAdapter(String name) {
		this.name=name;
	}
	public void StudyGrade1(){}
	public void StudyGrade2(){}
	public void StudyGrade3(){}
	public void StudyGrade4(){}
	public void TheisPresentation() {
		System.out.println("我是"+name);
		StudyGrade1();
		StudyGrade2();
		StudyGrade3();
		StudyGrade4();
	}
}
        4.2.4 main方法实现缺省适配器模式
package Adapter.defualtAdapter.Student;

public class Test {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		StudyAdapter s= new GeneralStudent("Emily");;//针对抽象目标接口编程
		s.TheisPresentation();
		System.out.println("-----------------------");
		StudyAdapter s1= new SpecialStudent("zz");;//针对抽象目标接口编程
		s1.TheisPresentation();
		System.out.println("-----------------------");
		StudyAdapter s2= new DiscountinueStudyStudent("xx");;//针对抽象目标接口编程
		s2.TheisPresentation();
	}

}
        4.2.5 UML图

        4.3 双向适配器
  • 1.灰太狼临死前他将自己的抓羊秘技传给了已经成年的小灰灰,并留下一件狼皮和一件羊皮。小灰灰也承担起了抓羊的重任;
  • 2.小灰灰和羊相爱了,但是在梁山上,狼只能一直披着羊皮生活;
  • 3.小灰灰带媳妇回去看妈妈。

        他们改造成了一个新型的法宝——狼羊双向适配器。把羊皮和狼皮缝在一起,正面羊皮,翻过来后就是狼皮,这样一件衣服,既能扮狼又能扮羊。

        4.3.1 目标(Target)角色(ISheep、IWolf:充当目标接口)
package Adapter.doubleAdapter.wolfsheep;
//目标角色
public interface ISheep {
	public void sheepLooks();
	public void eatGrass();
}
package Adapter.doubleAdapter.wolfsheep;
//目标角色
public interface IWolf {
	public void wolfLooks();
	public void eatMeat();
}
        4.3.2 适配者(Adaptee)角色(Sheep、Wolf:充当适配者)
package Adapter.doubleAdapter.wolfsheep;
//适配者
public class Sheep implements ISheep {

	@Override
	public void sheepLooks() {
		System.out.println("I am a sheep,too!");
	}

	@Override
	public void eatGrass() {
		System.out.println("I eat grass!");
	}

}
package Adapter.doubleAdapter.wolfsheep;
//适配者
public class Wolf implements IWolf {

	@Override
	public void wolfLooks() {
		System.out.println("I am a wolf,too!");
	}

	@Override
	public void eatMeat() {
		System.out.println("I eat meat!");
	}

}
        4.3.3 适配器(Adapter)角色(TwoFaceAdapter:充当适配器)
package Adapter.doubleAdapter.wolfsheep;
//适配器
public class TwoFaceAdapter implements ISheep, IWolf {
	ISheep sheep =null;
	IWolf wolf =null;
	public TwoFaceAdapter(ISheep sheep) {
		this.sheep=sheep;
	}
	public TwoFaceAdapter(IWolf wolf) {
		this.wolf=wolf;
	}
	@Override
	public void wolfLooks() {
		System.out.println("I am a wolf,too!");
	}

	@Override
	public void eatMeat() {
		// TODO 自动生成的方法存根		
		sheep.eatGrass();
	}

	@Override
	public void sheepLooks() {
		// TODO 自动生成的方法存根
		System.out.println("I am a sheep,too!");
	}

	@Override
	public void eatGrass() {
		// TODO 自动生成的方法存根
		wolf.eatMeat();
	}

}
        4.3.4 main方法实现双向适配器模式
package Adapter.doubleAdapter.wolfsheep;

public class Test {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		//把羊设配成狼
		ISheep fakersheep = new TwoFaceAdapter(new Wolf());
		fakersheep.sheepLooks();
		fakersheep.eatGrass();
		//把狼设配成羊
		IWolf fakerwolf = new TwoFaceAdapter(new Sheep());
		fakerwolf.wolfLooks();
		fakerwolf.eatMeat();
	}

}
        4.3.5 UML图

五、代码结构图

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

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

相关文章

【漏洞复现】云时空社会化商业ERP fileupload/gpy存在任意文件上传漏洞

漏洞描述 云时空社会化商业ERP fileupload/gpy存在任意文件上传漏洞 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行…

最邻近插值和线性插值

最邻近插值 在图像分割任务中:原图的缩放一般采用双线性插值,用于上采样或下采样;而标注图像的缩放有特定的规则,需使用最临近插值,不用于上采样或下采样。 自定义函数 这个是通过输入原始图像和一个缩放因子来对图像…

面试算法准备:树

这里写目录标题 1.树的基础1.1 首次理解1.2 深入理解1.2.1后序位置的特殊之处1.2.2 二叉树的思维指导 1.3 层序遍历1.4 二叉搜索树 BST 2.二叉树例题2.1 树的最大深度2.2 二叉树的直径2.3 二叉树的翻转2.4 填充每个节点的下一个右侧节点指针2.5 二叉树展开为链表 3 BST例题3.1 …

findImg找图工具

findImg 安装 npm install findImg -g 启动 findImg run 介绍 找出当前目录下的所有图片(包括svg的symbol格式)在浏览器中显示出来 源码 https://github.com/HuXin957/find-img 场景 例如前端项目中的img目录,大家都在往里面放图片&#xff…

9月BTE第8届广州国际生物技术大会暨展览会,全媒体聚焦下的高精尖行业盛会

政策春风助力,共迎大湾区生物医药行业50亿红利 今年3月“创新药”首次写入国务院政府工作报告之后,广州、珠海、北京多地政府纷纷同步出台了多项细化政策,广州最高支持额度高达50亿元,全链条为生物医药产业提供资金支持&#xff…

力扣:104. 二叉树的最大深度(Java,DFS,BFS)

目录 题目描述:输入:输出:代码实现:1.深度优先搜索(递归)2.广度优先搜索(队列) 题目描述: 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从…

排序 “壹” 之插入排序

目录 ​编辑 一、排序的概念 1、排序: 2、稳定性: 3、内部排序: 4、外部排序: 二、排序的运用 三、插入排序算法实现 3.1 基本思想 3.2 直接插入排序 3.2.1 排序过程: 3.2.2 代码示例: 3.2.3…

PMP每年考几次,费用如何?

今年的的考试分别分布在3月、6月、8月、11月,一般来说PMP的考试时间是3、6、9、12月,如果有特殊情况PMI也会及时进行调整,具体看他们官网的通知了。 PMP的考试费用全球是统一的,在国内考试报名费用是3900元,如果考试没…

JVM类加载基本流程及双亲委派模型

1.JVM内存区域划分 一个运行起来的Java进程就是一个JVM虚拟机,这就需要从操作系统中申请一片内存区域。JVM申请到内存之后,会把这个内存划分为几个区域,每个区域都有各自的作用。 一般会把内存划分为四个区域:方法区(也称 "…

在PostgreSQL中,如何创建一个触发器并在特定事件发生时执行自定义操作?

文章目录 解决方案示例代码1. 创建自定义函数2. 创建触发器 解释 在PostgreSQL中,触发器(trigger)是一种数据库对象,它能在特定的事件(如INSERT、UPDATE或DELETE)发生时自动执行一系列的操作。这些操作可以…

基于SSM,JSP超市进销存管理系统

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 权限划分:用户管理员 用户: 登录,注销,查看基本信息,修改基本信息 进货管理: 进货信息:可以新增进货,查询进货&#xff0…

GRAF: Generative Radiance Fields for 3D-Aware Image Synthesis

GRAF: Generative Radiance Fieldsfor 3D-Aware Image Synthesis(基于产生辐射场的三维图像合成) 思维导图:https://blog.csdn.net/weixin_53765004/article/details/137944206?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3…

突破速率界限:800G光模块的兴起

在以ChatGPT和NVIDIA DGX H200为代表的技术取得显著进步的时代,人工智能行业同样表现出明显地提升。除此之外,一项改变传统规则的创新出现了:800G光模块。这类优质的设备预示着数据传输和接收领域的变革性转变,成功引起了人们的兴…

【系统架构师】-案例考点(一)

1、软件架构设计 主要考点: 质量属性、软件架构风格、软件架构评估、MVC架构、面向服务的SOA架构、 DSSA、ABSD 1.1、质量属性 1、性能:指系统的响应能力,即要经过多长时间才能对某个事件做出响应,或者在某段时间内系统所能处理的事件的…

利用AQS(AbstractQueuedSynchronizer)实现一个线程同步器

目录 1. 前言 2. 什么是同步器 3. 同步器实现思路 Semaphore(信号量) 4. 代码实现 4.1. 创建互斥锁类 4.2 编写静态内部类,继承AQS 4.3 内部类实现AQS钩子函数 4.3 封装lock,unlock方法 4.4. 测试 5. 总结 本文章源码仓库:Conc…

FPGA - 基于自定义AXI FULL总线的PS和PL交互

前言 在FPGA - ZYNQ 基于Axi_Lite的PS和PL交互中,介绍了基于基于AXi_Lite的PL和PS交互,接下来构建基于基于Axi_Lite的PS和PL交互。 AXI_GP、AXI_HP和AXI_ACP接口 首先看一下ZYNQ SoC的系统框图,如下图所示。在图中,箭头方向代表…

Python 中整洁的并行输出

原文:https://bernsteinbear.com/blog/python-parallel-output/ 代码:https://gist.github.com/tekknolagi/4bee494a6e4483e4d849559ba53d067b Python 并行输出 使用进程和锁并行输出多个任务的状态。 注:以下代码在linux下可用&#xff0c…

Tcpdump -r 解析pcap文件

当我们使用命令抓包后,想在命令行直接读取筛选怎么办?-r参数就支持了这个 当你使用 tcpdump 的 -r 选项读取一个之前捕获的数据包文件,并想要筛选指定 IP 地址和端口的包时,你可以在命令中直接加入过滤表达式。这些过滤表达式可以…

数据可视化(六):Pandas爬取NBA球队排名、爬取历年中国人口数据、爬取中国大学排名、爬取sina股票数据、绘制精美函数图像

Tips:"分享是快乐的源泉💧,在我的博客里,不仅有知识的海洋🌊,还有满满的正能量加持💪,快来和我一起分享这份快乐吧😊! 喜欢我的博客的话,记得…

基于ThinkPHP框架开发的的站长在线工具箱网站PHP源码(可以作为流量站)

这是一套基于ThinkPHP框架开发的站长在线工具箱网站PHP源码,包含了多种在线工具,可以作为流量站使用。 项 目 地 址 : runruncode.com/php/19742.html 部署教程: 环境要求: - PHP版本需要大于等于7.2.5 - MySQL版…