zoukankan      html  css  js  c++  java
  • 适配器模式

    适配器模式(Adapter Pattern)

    将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

    在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。

    根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

    介绍

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

    主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

    何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

    如何解决:继承或依赖(推荐)。

    关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。

    应用实例: 1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。 4、JAVA 中的 jdbc。

    优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

    缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

    使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

    注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

    角色

    Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

    Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

    Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

    示例

    类适配器

    首先有一个已存在的将被适配的类

    1 public class Adaptee {
    2     public void adapteeRequest() {
    3         System.out.println("被适配者的方法");
    4     }
    5 }

    定义一个目标接口

    1 public interface Target {
    2     void request();
    3 }

    如果通过一个适配器类,实现 Target 接口,同时继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adapteeRequest()

    1 public class Adapter extends Adaptee implements Target{
    2     @Override
    3     public void request() {
    4         //...一些操作...
    5         super.adapteeRequest();
    6         //...一些操作...
    7     }
    8 }

    测试

    1 public class Test {
    2     public static void main(String[] args) {
    3         Target target = new ConcreteTarget();
    4         target.request();
    5 
    6         Target adapterTarget = new Adapter();
    7         adapterTarget.request();
    8     }
    9 }

    结果如下:

    1 concreteTarget目标方法
    2 被适配者的方法

    这样我们即可在新接口 Target 中适配旧的接口或类

    对象适配器

    对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器

     1 public class Adapter implements Target{
     2     // 适配者是对象适配器的一个属性
     3     private Adaptee adaptee = new Adaptee();
     4 
     5     @Override
     6     public void request() {
     7         //...
     8         adaptee.adapteeRequest();
     9         //...
    10     }
    11 }

    注意这里的 Adapter 是将 Adaptee 作为一个成员属性,而不是继承它

    电压适配器

    再来一个好理解的例子,我们国家的民用电都是 220V,日本是 110V,而我们的手机充电一般需要 5V,这时候要充电,就需要一个电压适配器,将 220V 或者 100V 的输入电压变换为 5V 输出

    定义输出交流电接口,输出220V交流电类和输出110V交流电类

     1 public class AC110 implements AC {
     2     public final int output = 110;
     3 
     4     @Override
     5     public int outputAC() {
     6         return output;
     7     }
     8 }
     9 
    10 public class AC220 implements AC {
    11     public final int output = 220;
    12 
    13     @Override
    14     public int outputAC() {
    15         return output;
    16     }
    17 }

    适配器接口,其中 support() 方法用于检查输入的电压是否与适配器匹配,outputDC5V() 方法则用于将输入的电压变换为 5V 后输出

    1 public interface DC5Adapter {
    2     boolean support(AC ac);
    3 
    4     int outputDC5V(AC ac);
    5 }

    实现中国变压适配器和日本变压适配器

     1 public class ChinaPowerAdapter implements DC5Adapter {
     2     public static final int voltage = 220;
     3     
     4     @Override
     5     public boolean support(AC ac) {
     6         return (voltage == ac.outputAC());
     7     }
     8     
     9     @Override
    10     public int outputDC5V(AC ac) {
    11         int adapterInput = ac.outputAC();
    12         //变压器...
    13         int adapterOutput = adapterInput / 44;
    14         System.out.println("使用ChinaPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
    15         return adapterOutput;
    16     }
    17 }
    18 
    19 public class JapanPowerAdapter implements DC5Adapter {
    20     public static final int voltage = 110;
    21 
    22     @Override
    23     public boolean support(AC ac) {
    24         return (voltage == ac.outputAC());
    25     }
    26 
    27     @Override
    28     public int outputDC5V(AC ac) {
    29         int adapterInput = ac.outputAC();
    30         //变压器...
    31         int adapterOutput = adapterInput / 22;
    32         System.out.println("使用JapanPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
    33         return adapterOutput;
    34     }
    35 }

    测试,准备中国变压适配器和日本变压适配器各一个,定义一个方法可以根据电压找到合适的变压器,然后进行测试

     1 public class Test {
     2     private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();
     3 
     4     public Test() {
     5         this.adapters.add(new ChinaPowerAdapter());
     6         this.adapters.add(new JapanPowerAdapter());
     7     }
     8 
     9     // 根据电压找合适的变压器
    10     public DC5Adapter getPowerAdapter(AC ac) {
    11         DC5Adapter adapter = null;
    12         for (DC5Adapter ad : this.adapters) {
    13             if (ad.support(ac)) {
    14                 adapter = ad;
    15                 break;
    16             }
    17         }
    18         if (adapter == null){
    19             throw new  IllegalArgumentException("没有找到合适的变压适配器");
    20         }
    21         return adapter;
    22     }
    23 
    24     public static void main(String[] args) {
    25         Test test = new Test();
    26         AC chinaAC = new AC220();
    27         DC5Adapter adapter = test.getPowerAdapter(chinaAC);
    28         adapter.outputDC5V(chinaAC);
    29 
    30         // 去日本旅游,电压是 110V
    31         AC japanAC = new AC110();
    32         adapter = test.getPowerAdapter(japanAC);
    33         adapter.outputDC5V(japanAC);
    34     }
    35 }

    结果:

    1 使用ChinaPowerAdapter变压适配器,输入AC:220V,输出DC:5V
    2 使用JapanPowerAdapter变压适配器,输入AC:110V,输出DC:5V

    适配器模式总结

    主要优点:

    1.将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。

    2.增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。

    3.灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

    具体来说,类适配器模式还有如下优点:

    由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

    对象适配器模式还有如下优点:

    一个对象适配器可以把多个不同的适配者适配到同一个目标;

    可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。

    类适配器模式的缺点如下:

    对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;

    适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;

    在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

    对象适配器模式的缺点如下:

    与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子

    类当做真正的适配者进行适配,实现过程较为复杂。

    适用场景:

    系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。

    想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

    适配器模式在Java中的应用及解读

    spring AOP中的适配器模式

    在Spring的Aop中,使用的 Advice(通知) 来增强被代理类的功能。

    Advice的类型有:MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice

    在每个类型 Advice 都有对应的拦截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor

    Spring需要将每个 Advice 都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对 Advice 进行转换

    三个适配者类 Adaptee 如下:

     1 public interface MethodBeforeAdvice extends BeforeAdvice {
     2     void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
     3 }
     4 
     5 public interface AfterReturningAdvice extends AfterAdvice {
     6     void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable;
     7 }
     8 
     9 public interface ThrowsAdvice extends AfterAdvice {
    10 }

    目标接口 Target,有两个方法,一个判断 Advice 类型是否匹配,一个是工厂方法,创建对应类型的 Advice 对应的拦截器

    1 public interface AdvisorAdapter {
    2     boolean supportsAdvice(Advice var1);
    3 
    4     MethodInterceptor getInterceptor(Advisor var1);
    5 }

    三个适配器类 Adapter 分别如下,注意其中的 Advice、Adapter、Interceptor之间的对应关系

     1 class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
     2     @Override
     3     public boolean supportsAdvice(Advice advice) {
     4         return (advice instanceof MethodBeforeAdvice);
     5     }
     6 
     7     @Override
     8     public MethodInterceptor getInterceptor(Advisor advisor) {
     9         MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    10         return new MethodBeforeAdviceInterceptor(advice);
    11     }
    12 }
    13 
    14 @SuppressWarnings("serial")
    15 class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
    16     @Override
    17     public boolean supportsAdvice(Advice advice) {
    18         return (advice instanceof AfterReturningAdvice);
    19     }
    20     @Override
    21     public MethodInterceptor getInterceptor(Advisor advisor) {
    22         AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
    23         return new AfterReturningAdviceInterceptor(advice);
    24     }
    25 }
    26 
    27 class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
    28     @Override
    29     public boolean supportsAdvice(Advice advice) {
    30         return (advice instanceof ThrowsAdvice);
    31     }
    32     @Override
    33     public MethodInterceptor getInterceptor(Advisor advisor) {
    34         return new ThrowsAdviceInterceptor(advisor.getAdvice());
    35     }
    36 }

    客户端 DefaultAdvisorAdapterRegistry

     1 public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
     2     private final List<AdvisorAdapter> adapters = new ArrayList(3);
     3 
     4     public DefaultAdvisorAdapterRegistry() {
     5         // 这里注册了适配器
     6         this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
     7         this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
     8         this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
     9     }
    10     
    11     public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    12         List<MethodInterceptor> interceptors = new ArrayList(3);
    13         Advice advice = advisor.getAdvice();
    14         if (advice instanceof MethodInterceptor) {
    15             interceptors.add((MethodInterceptor)advice);
    16         }
    17 
    18         Iterator var4 = this.adapters.iterator();
    19 
    20         while(var4.hasNext()) {
    21             AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
    22             if (adapter.supportsAdvice(advice)) {   // 这里调用适配器方法
    23                 interceptors.add(adapter.getInterceptor(advisor));  // 这里调用适配器方法
    24             }
    25         }
    26 
    27         if (interceptors.isEmpty()) {
    28             throw new UnknownAdviceTypeException(advisor.getAdvice());
    29         } else {
    30             return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);
    31         }
    32     }
    33     // ...省略...
    34 }    

    这里看 while 循环里,逐个取出注册的适配器,调用 supportsAdvice() 方法来判断 Advice 对应的类型,然后调用 getInterceptor() 创建对应类型的拦截器

    这里应该属于对象适配器模式,关键字 instanceof 可看成是 Advice 的方法,不过这里的 Advice 对象是从外部传进来,而不是成员属性。

    spring MVC中的适配器模式

    Spring MVC中的适配器模式主要用于执行目标 Controller 中的请求处理方法。

    在Spring MVC中,DispatcherServlet 作为用户,HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。

    为什么要在 Spring MVC 中使用适配器模式?Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet 直接获取对应

    类型的 Controller,需要的自行来判断,像下面这段代码一样:

    1 if(mappedHandler.getHandler() instanceof MultiActionController){  
    2    ((MultiActionController)mappedHandler.getHandler()).xxx  
    3 }else if(mappedHandler.getHandler() instanceof XXX){  
    4     ...  
    5 }else if(...){  
    6    ...  
    7 }  

    这样假设如果我们增加一个 HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController),这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,

    对修改关闭。

    我们来看看源码,首先是适配器接口 HandlerAdapter

    1 public interface HandlerAdapter {
    2     boolean supports(Object var1);
    3 
    4     ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    5 
    6     long getLastModified(HttpServletRequest var1, Object var2);
    7 }

    现该接口的适配器每一个 Controller 都有一个适配器与之对应,这样的话,每自定义一个 Controller 需要定义一个实现 HandlerAdapter 的适配器。

    springmvc 中提供的 Controller 实现类有如下

    springmvc 中提供的 HandlerAdapter 实现类如下

    HttpRequestHandlerAdapter 这个适配器代码如下

     1 public class HttpRequestHandlerAdapter implements HandlerAdapter {
     2     public HttpRequestHandlerAdapter() {
     3     }
     4 
     5     public boolean supports(Object handler) {
     6         return handler instanceof HttpRequestHandler;
     7     }
     8 
     9     public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    10         ((HttpRequestHandler)handler).handleRequest(request, response);
    11         return null;
    12     }
    13 
    14     public long getLastModified(HttpServletRequest request, Object handler) {
    15         return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
    16     }
    17 }

    当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet 会通过 handler 的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通

    过适配器的 hanle() 方法来调用 Controller 中的用于处理请求的方法。

     1 public class DispatcherServlet extends FrameworkServlet {
     2     private List<HandlerAdapter> handlerAdapters;
     3     
     4     //初始化handlerAdapters
     5     private void initHandlerAdapters(ApplicationContext context) {
     6         //..省略...
     7     }
     8     
     9     // 遍历所有的 HandlerAdapters,通过 supports 判断找到匹配的适配器
    10     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    11         for (HandlerAdapter ha : this.handlerAdapters) {
    12             if (logger.isTraceEnabled()) {
    13                 logger.trace("Testing handler adapter [" + ha + "]");
    14             }
    15             if (ha.supports(handler)) {
    16                 return ha;
    17             }
    18         }
    19     }
    20     
    21     // 分发请求,请求需要找到匹配的适配器来处理
    22     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    23         HttpServletRequest processedRequest = request;
    24         HandlerExecutionChain mappedHandler = null;
    25 
    26         // Determine handler for the current request.
    27         mappedHandler = getHandler(processedRequest);
    28             
    29         // 确定当前请求的匹配的适配器.
    30         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    31 
    32         ha.getLastModified(request, mappedHandler.getHandler());
    33                     
    34         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    35     }
    36     // ...省略...
    37 }

    通过适配器模式我们将所有的 controller 统一交给 HandlerAdapter 处理,免去了写大量的 if-else 语句对 Controller 进行判断,也更利于扩展新的 Controller 类型。

  • 相关阅读:
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节4(集成Web)——待补充
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节5(集成安全框架)——待补充
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节6(数据库)
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节7(校验功能)——待补充
    其他(Excel函数集团)
    MM函数(Excel函数集团)
    Offset函数(Excel函数集团)
    Lookup函数(Excel函数集团)
    Indirect函数(Excel函数集团)
    查找与引用函数(Excel函数集团)
  • 原文地址:https://www.cnblogs.com/xiaojiesir/p/11100512.html
Copyright © 2011-2022 走看看