我所知道设计模式之适配器模式

28640 2021-01-22 15:29:09
设计 适配器 模式 知道 所知


前言介绍


接下里介绍的是Java 的设计模式之一:适配器模式

如果你是第一次出国到美国旅行, 你会发现美国电源插头和插座标准与中国不同。这时你需要购买美国的电源插头与插座。

要是第二次出国到德国,到了德国又发现美国插头和德国插座不匹配

image.png

这时我们需要购买同时提供美国标准插座和欧洲标准插头的电源适配器可以解决这个难题

一、什么是适配器模式

适配器模式(Adapter Pattern):将某个类的接口转换成客户端期望的另一个接口表示

主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)

适配器模式属于结构型模式

主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

二、工作原理


适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容

从用户的角度看不到被适配者,是解耦的

用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法

用户收到反馈结果,感觉只是和目标接口交互,如图

image.png

三、类适配器模式

有一个Adapter通过继承src类,实现dst类接口,完成src->dst的适配。

应用实例说明

以生活中充电器的例子来讲解适配器:

  • 充电器本身相当于 Adapter
  • 220V 交流电相当于 src (即被适配者)
  • 我们的目 dst(即目标)是 5V 直流电压

image.png

根据类适配器的思路,我们的类图是这样的情况

image.png

按照我们的思路,先创建一个220v的电源类输出电压

//被适配的类
class Voltage220V {
//输出 220V 的电压
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}

接下里我们提供手机使用的5v电压,进行输出

//适配接口
interface IVoltage5V {
public int output5V();
}

这样我们就需要一个适配器类将220v降压转为5v的电压

//适配器类
class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
//获取到 220V 电压
int srcV = output220V();
int dstV = srcV / 44 ; //降压处理转成 5v
return dstV;
}
}

接下里我们就可以让手机通过适配器,进行充电啦

class Phone {
//充电
public void charging(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压为 5V, 可以充电~~");
} else if (iVoltage5V.output5V() > 5) {
System.out.println("电压大于 5V, 不能充电~~");
}
}
}

接下来让我们看看这样demo是怎么进行的呢?

public static void main(String[] args) {
System.out.println(" === 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
运行结果如下:
=== 类适配器模式 ====
电压=220伏
电压为 5V, 可以充电~~

类适配器模式注意事项和细节

Java 是单继承机制,所以类适配器需要继承 src 类这一点算是一个缺点, 因为这要求 dst 必须是接口,有一定局限性;

src 类的方法在 Adapter 中 都会暴露出来,也增加了使用的成本。

但是其继承了 src 类,所以它可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强了。

四、对象适配器

在之前讲解的类适配器的时候,发现需要继承 src 类,添加了耦合度

而对象适配器和类适配器模式相同,只是将 Adapter 类作进行修改

我们将不是继承 src 类,而是持有 src 类的实例,以解决兼容性的问题。

即:持有 src 类,实现 dst 类接口,完成 src->dst 的适配

根据“合成复用原则”,在系统中尽量使用关联关系(聚合)来替代继承关系

根据对象适配器的思路,我们的类图是这样的情况

image.png

根据我们的思路,原先输出5v的接口不动,还是原来的样子

//适配接口
interface IVoltage5V {
public int output5V();
}

并且原先一个220v的电源类输出电压也是无需改变的样子

//被适配的类
class Voltage220V {
//输出 220V 的电压
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}

接下来我们修改原先的适配器类,添加构造器传入220v输出类

//适配器类
class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
int dstV = 0;
if(null != voltage220V){
//获取到 220V 电压
int srcV = voltage220V.output220V();
dstV = srcV / 44 ; //降压处理转成 5v
System.out.println("适配完成,输出的电压为:" +dstV);
}
return dstV;
}
}

接下里我们就可以让手机通过适配器,进行充电啦

class Phone {
//充电
public void charging(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压为 5V, 可以充电~~");
} else if (iVoltage5V.output5V() > 5) {
System.out.println("电压大于 5V, 不能充电~~");
}
}
}

接下来让我们看看这样demo是怎么进行的呢?

public static void main(String[] args) {
System.out.println(" === 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
运行结果如下:
=== 类适配器模式 ====
电压=220伏
适配完成,输出的电压为:5
电压为 5V, 可以充电~~

对象适配器模式注意事项和细节

对象适配器和类适配器其实算是同一种思想,只不过实现方式不同

根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst必须是接口。

使用成本更低,更灵活

五、接口适配器模式

一些书籍称为:缺省适配器模式(有选择性)

核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求

适用于一个接口不想使用其所有的方法的情况

应用实例说明

在Android 中的属性动画 ValueAnimator 类可以通过addListener(AnimatorListener listener)方法添加监听器

一般写的写法如下:

ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
valueAnimator.addListener(new Animator.AnimatorListenerO{
//默认实现(空方法)
@Overmride
public void onAnimationStant(Animator animation){}
//默认实现(空方法)
@Ovemide
public void onAnimationEnd(Animator animation){}
//默认实现(空方法)
@Overide
public void onAnimationCancel(Animator animation){}
//默认实现(空方法)
@Ovemide
public woid onAnimationRepeat(Animator animation){}
});

有时候我们不想实现Animator.AnimatorListener 接口的全部方法,我们只想监听onAnimationStart,我们会如下写

ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
valueAnimator.addListener(new Animator.AnimatorListenerO{
//默认实现(空方法)
@Overmride
public void onAnimationStant(Animator animation){}
});

诶,为什么我们可以这样,原因在于有一个抽象类默认实现了空方法

首先是我们的接口:AnimatorListener

public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}

接下来使我们的抽象类:AnimatorListenerAdapter

public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
Animator.AnimatorPauseListener {
//默认实现(空方法)
@Overmride
public void onAnimationStant(Animator animation){}
//默认实现(空方法)
@Ovemide
public void onAnimationEnd(Animator animation){}
//默认实现(空方法)
@Overide
public void onAnimationCancel(Animator animation){}
//默认实现(空方法)
@Ovemide
public woid onAnimationRepeat(Animator animation){}
}

也就是说做了一个抽象类,实现了两个接口,并且以默认实现空方法

这就是为什么我们可以只写start方法也可以,重写自己关心的方法

那么我们的类图,其实就是这样的,可如下图所示

image.png

接下来我们使用demo 体会体会,首先先创一个接口

interface Interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
}

同时我们根据思路创建Adapter抽象类继承这个接口,默认实现空方法

//在 AbsAdapter 我们将 Interface4 的方法进行默认实现
abstract class AbsAdapter implements Interface4 {
//默认实现
@Override
public void m1() {}
@Override
public void m2() {}
@Override
public void m3() {}
@Override
public void m4() {}
}

当我们只需要实现m1的时候,可以看看demo是怎么做的?

public static void main(String[] args) {
AbsAdapter absadapter = new AbsAdapter() {
@Override
public void m1() {
System.out.println("使用m1的方法");
}
};
absadapter.m1();
}
运行结果如下:
使用m1的方法

这里我们就采用了匿名内部类的方式去实现m1即可,无需全部实现

匿名内部类的定义

先看匿名内部类的定义的语法格式:

new 实现接口(){//匿名内部类类体部分}
new 父类构造器(实参列表){//匿名内部类类体部分}

这两种方式的定义分别对应两种方式,一种是接口,另一种是抽象类。

对于实现接口,由于接口是没有构造函数的,注意这里一定是空参数。

第二种是调用父类的构造器,注意此处可以是空参数,也可以传入参数

参考资料


尚硅谷:设计模式(韩顺平老师):适配器模式

Refactoring.Guru:《深入设计模式》

版权声明
本文为[28640]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000039061378

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云