设计模式-适配器模式( Adapter Pattern)

设计模式-适配器模式( Adapter Pattern)

   概要

   记忆关键词:转换,兼容接口

   定义:将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

   分析:在适配器模式中,客户端调用适配器以获得相应功能,适配器扩展适配者以实现对应功能。

   类型:结构型

   适配器模式结构图如下:

   一、涉及相关的角色

   1. 目标(Target)接口 - 客户需求接口

   当前系统业务所期待的接口,它可以是抽象类或接口。

   2.  适配者(Adaptee)类 - 现有接口 

   它是被访问和适配的现存组件库中的组件接口。

   3. 适配器(Adapter)类

   它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

   二、适配器两种模式

   适配器模式有两种形式对象适配器和类适配器。下面通过举例说明。

   美国的正常供电电压为110V,一个中国人带了一款中国制造电器去美国,这个电器必须要在220电压下才能充电使用。 

   分析:这种情况下,客户(中国人)的期望接口是有一个220V的电压为电器充电,但实际的接口是仅有一个110V的电压供电器充电,这种情况下就需要采用一根电压转换器(适配器)使得110V的电压能够转换为220V的电压,供客户使用。

   适配者 Adaptee:

1 /**
2  * 现有接口——只能通过110V电压充电
3  */
4 public interface Adaptee {
5     void chargeBy110V();
6 }

   适配者实现类:

1 public class AmericanCharger implements Adaptee{
2     @Override
3     public void chargeBy110V() {
4         System.out.println("美国供电器,只为你服务,正在通过110V电压为您充电");
5     }
6 }

   目标接口 Target:

1 /**
2  * 客户期望的接口——220V的电压充电
3  */
4 public interface Target {
5     void chargeBy220V();
6 }

   1.  类适配器 

 1 /**
 2  * 类适配器
 3  */
 4 public class Adapter extends AmericanCharger implements Target {
 5     @Override
 6     public void chargeBy220V() {
 7         //现有功能
 8         super.chargeBy110V();
 9 
10         //对现有功能扩展
11         System.out.println("再加110V,达到220V,冲鸭!");
12     }
13

   客户端类:

 1 public class Client1 {
 2     public static void main(String[] args) {
 3         //类适配器使得代码逻辑混乱
 4         //这种情况下仿佛Adapter是一种110V的美国供电器可以直接使用不需要其他信息
 5         //具体可以和对象适配器对比以下
 6         new Adapter().chargeBy220V();
 7 
 8     }
 9 }
10 
11 
12 //运行结果
13 美国供电器,只为你服务,正在通过110V电压为您充电
14 再加110V,达到220V,冲鸭!

   2.  对象适配器

 1 /**
 2  * 对象适配器
 3  */
 4 public class Adapter implements Target{
 5     Adaptee adaptee;
 6 
 7     public Adapter(Adaptee adaptee) {
 8         this.adaptee = adaptee;
 9     }
10 
11     @Override
12     public void chargeBy220V() {
13          adaptee.chargeBy110V();
14     }
15 }

   客户端类:

 1 public class Client2 {
 2     public static void main(String[] args) {
 3 
 4         //现在我们有一个美国110V供电站,但我们无法正常使用
 5         AmericanCharger americanCharger = new AmericanCharger();
 6 
 7         //我们将这个供电器交给适配器,适配器转换为220V供电器
 8         Adapter adapter = new Adapter(americanCharger);
 9 
10         //接下来我们通过适配器充电就好了
11         adapter.chargeBy220V();
12     }
13 }
14 
15 //运行结果表
16 美国供电器,只为你服务,正在通过110V电压为您充电

   三、优缺点分析

   1. 优点

  •   可以让任何两个没有关联的类一起运行。
  •   提高了类的复用,可以一致化多个不同接口。
  •   将现有接口实现类隐藏,增加了类的透明度。
  •   灵活性高,可自由适配。

   2. 缺点

  •   过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。
  •   因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
  •   某些适配工作可能非常困难,例如让房子飞起来。
  •    当我们有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

   四、应用场景

   DisposableBeanAdapter 是 Spring 框架中适配器模式的一个实际应用。

   它通过适配不同的销毁方法(DisposableBean 接口、自定义销毁方法),实现了统一管理 bean 销毁过程的功能,体现了适配器模式的核心思想:将不同的接口适配为统一的接口,从而简化和统一处理逻辑

   摘取了部分代码如下:

 1 class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
 2 
 3     private final Object bean;
 4 
 5     //...
 6 
 7     @Override
 8     public void destroy() {
 9         // 1. 执行所有 BeanPostProcessors 的 postProcessBeforeDestruction 方法
10         if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
11             for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
12                 processor.postProcessBeforeDestruction(this.bean, this.beanName);
13             }
14         }
15 
16         // 2. 调用 DisposableBean 的 destroy 方法
17         if (this.invokeDisposableBean) {
18             if (logger.isTraceEnabled()) {
19                 logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
20             }
21             try {
22                 if (System.getSecurityManager() != null) {
23                     AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
24                         ((DisposableBean) this.bean).destroy();
25                         return null;
26                     }, this.acc);
27                 } else {
28                     ((DisposableBean) this.bean).destroy();
29                 }
30             } catch (Throwable ex) {
31                 String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
32                 if (logger.isDebugEnabled()) {
33                     logger.warn(msg, ex);
34                 } else {
35                     logger.warn(msg + ": " + ex);
36                 }
37             }
38         }
39 
40         // 3. 调用自定义销毁方法
41         if (this.destroyMethod != null) {
42             invokeCustomDestroyMethod(this.destroyMethod);
43         } else if (this.destroyMethodName != null) {
44             Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
45             if (methodToInvoke != null) {
46                 invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
47             }
48         }
49     }
50 
51 }

   五、注意事项

   适配器不是在详细设计时添加的,而是解决正在服役的项目的问题,即现有接口可能无法改变(去美国不可能把人家110V电压供给改成220V电压供给)。 

 

   参考链接:

   https://www.cnblogs.com/study-hard-forever/p/13167161.html

posted @ 2024-07-31 22:28  欢乐豆123  阅读(34)  评论(0编辑  收藏  举报