java设计模式 -- 适配器模式

513 阅读5分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

1.什么是适配器模式

适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。这种类型的设计模式属于结构型模式。

举个例子:在生活中我们的家用电压一般是220V,我们手机的充电电压通常是5v,如果直接把家用电压接到电池接到电池两极直接充电的话,电池会烧掉。这个时候我们的充电就扮演一个适配器的角色,使得家用电源能够安全的给手机电池充电

2.适配器模式原理

2.1组成

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

  1. 目标(Target)接口:当前系统业务所期望的接口,它可以是具体的类、抽象类或接口。
  2. 适配者(Adaptee)类:它是被适配的对象,已有接口,但和目标接口不兼容。
  3. 适配器(Adapter)类:它是一个转换器,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

2.2 分类

适配器模式(Adapter Pattern)主要分为三类

  1. 类适配器模式
  2. 对象适配器模式
  3. 接口适配器模式

3 类适配器模式

3.1 原理

适配器(Adapter)类通过继承适配者(Adaptee)类,并实现目标(Target)接口,完成适配者类到目标接口的转换。下面代码以给手机充电为例

3.2 类图

图片.png

3.3 核心代码

目标接口:

//目标接口
public interface IVoltage5V {
   public int output5V();
}

适配者:

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

适配器:

//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

   @Override
   public int output5V() {
      //获取到220V电压
      int srcV = output220V();
      int dstV = srcV / 44 ; //转成 5v
      return dstV;
   }

}

客户端:

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

3.4 总结

  1. Java是单继承的,他要继承适配者类,所以目标接口必须要是接口类,有一定局限性。
  2. 因为适配器继承了适配者类,所以会导致适配器中可以直接访问适配者类的一些其他方法或变量,增加使用成本,但同样地它也可以重写这些方法。

4.对象适配器模式

4.1 原理

与上面类适配器模式差不多,不同的是,适配器是通过持有被适配类(适配者)的实例,如以成员变量的方式持有,然后实现目标接口,从而完成适配者 ---》 目标接口的兼容。

4.2 类图

图片.png

4.3 核心代码

这里只有适配者和适配器的引用关系发生变化,所以我只贴出这部分代码 适配者(也无需改变)

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

适配器

//适配器类
public class VoltageAdapter  implements IVoltage5V {

   private Voltage220V voltage220V; // 关联关系-聚合
   
   //通过构造器,传入一个 Voltage220V 实例
   public VoltageAdapter(Voltage220V voltage220v) {
      
      this.voltage220V = voltage220v;
   }

   @Override
   public int output5V() {
      int dst = 0;
      if(null != voltage220V) {
         int src = voltage220V.output220V();//获取220V 电压
         System.out.println("使用对象适配器,进行适配~~");
         dst = src / 44;
         System.out.println("适配完成,输出的电压为=" + dst);
      }
      
      return dst;
      
   }

}

4.4 总结

  1. 对象适配器和类适配器本质上思想差不多,只不过实现方式不同。根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承适配者类的局限性问题,同时也不在要求目标接口类必须是接口。
  2. 是相对于类适配器一种更灵活的实现方式。

5.缺省适配器模式

5.1 原理

缺省适配器模式(接口适配器模式):缺省适配模式为一个接口提供缺省实现,通常是一个抽象类作为中间层实现接口所有的方法,默认为空实现,(在jdk1.8之后,接口也可以实现接口,给一个默认方法),这样我们就可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。当原接口中定义的方法太多,而其中大部分又不被需要时,这种模式非常实用。

5.2 类图

图片.png

5.3 核心代码

接口

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

抽象类

//在AbsAdapter 我们将 Interface4 的方法进行默认实现
public abstract class AbsAdapter implements Interface4 {

   //默认实现
   public void m1() {
   }

   public void m2() {
   }

   public void m3() {
   }

   public void m4() {
   }
}

//继承类

public class AbsAdapterImpl extends AbsAdapter{
    @Override
    public void m1() {
        System.out.println("我选择性实现了 m1()方法");
    }
}

5.4 总结

适用于当原接口中定义的方法太多,而其中大部分又不被需要时,使用这种模式选择性的实现一些方法。

6 应用案例

SpringMvc中的HandlerAdapter。

public class DispatcherServlet extends FrameworkServlet {
@Nullable
private List<HandlerAdapter> handlerAdapters;


//把所有的HandlerAdapter放到List<HandlerAdapter>中去
private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;
    if (this.detectAllHandlerAdapters) {
        Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    } else {
        try {
            HandlerAdapter ha = (HandlerAdapter)context.getBean("handlerAdapter", HandlerAdapter.class);
            this.handlerAdapters = Collections.singletonList(ha);
        } catch (NoSuchBeanDefinitionException var3) {
        }
    }

    if (this.handlerAdapters == null) {
        this.handlerAdapters = this.getDefaultStrategies(context, HandlerAdapter.class);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No HandlerAdapters declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");
        }
    }

}

//通过handlerMappings 给每个controller  分配对应的 handle
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        Iterator var2 = this.handlerMappings.iterator();

        while(var2.hasNext()) {
            HandlerMapping mapping = (HandlerMapping)var2.next();
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }

    return null;
}


//遍历 找到符合条件的适配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        Iterator var2 = this.handlerAdapters.iterator();

        while(var2.hasNext()) {
            HandlerAdapter adapter = (HandlerAdapter)var2.next();
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }

    throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

// 通过HandlerAdapter处理 返回对应的 ModelAndView 对象 通过HandlerAdapter把handles


mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


}

HandlerAdapter 的实现子类使得每一种Controller有一种对应的适配器实现,每种Controller 有不同的实现方式,扩展controller时,只需要增加HandlerAdapter的实现类即可。 图片.png