摘要:根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联聚合关系在类适配器模式中,适配器与适配者之间是继承或实现关系。
1、不同国家的插座是有区别的,如果我们去国外旅游,需要带上国外的插头转换器,来能兼容国外的插座;
2、手机的耳机孔有圆头和扁头,如果扁头的耳机孔想接圆头的耳机就需要一个耳机的转换器;
上述所说的转换器,其实就是适配器;它是用来做兼容的;
适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联(聚合)关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类—适配接口):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
以生活中充电器为例,充电器本身相当于适配者 (Adapter),220V 交流电相当于被适配者,我们的目标(target) 想把220V交流电转成5V直流电
要适配的类,即需要将220v电压转化为5v电压
//中国的电压220Vpublic class ChinaPower{ private final Integer outPut=220; public Integer getOutPut() { return outPut; }}
适配器接口,只负责定义转化需要使用的业务逻辑方法,具体实现交由适配器类完成
//将电压转化为5v---适配接口public interface TransTo5V{ Integer transTo5V();}
适配器类,继承了ChinaPower,并实现了适配器接口,负责实现讲220v电压转化为5v的具体业务逻辑代码实现
//适配器类---实现适配器接口public class ChinaAdapter extends ChinaPower implements TransTo5V{ //将220v电压转换为5v的 @Override public Integer transTo5V() { //获得被适配类,即我们需要将220v电压转化为5v返回 Integer output=super.getOutPut(); //进行电压转换操作 return output/44; }}
Phone类,需要用到适配器进行兼容,这样才可以充电
//手机需要5v的电压进行充电public class Phone{ //通过适配器获得5v的电压 public void charging(ChinaAdapter chinaAdapter) { if(5==chinaAdapter.transTo5V()) { System.out.println("得到5v,充电中..."); } else { System.out.println("电压过高,手机压力过大"); } }}
充电测试
public class test{ @Test public void test() { Phone p=new Phone(); p.charging(new ChinaAdapter()); }}
还是以上面的例子为例,这一次适配器类不再继承ChinaPower ,而是以聚合的方式来代替继承,符合设计模式中的"合成复用原则";java是单继承机制,这样可以保留对象继承权;
我们只需要修改适配器类即可:
//适配器类---实现适配器接口public class ChinaAdapter implements TransTo5V{ private ChinaPower chinaPower; //通过构造器,完成赋值 public ChinaAdapter(ChinaPower chinaPower) { this.chinaPower=chinaPower; } //将220v电压转换为5v的 @Override public Integer transTo5V() { //获得被适配类,即我们需要将220v电压转化为5v返回 Integer output=chinaPower.getOutPut(); //进行电压转换操作 return output/44; }}
测试
public class test{ @Test public void test() { Phone p=new Phone(); p.charging(new ChinaAdapter(new ChinaPower())); }}
对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承被适配者的局限性问题;
定义一个适配器接口:
public interface InterfaceTest { public void m1(); public void m2(); public void m3(); public void m4();}
抽象类 AbsAdapter 将 InterfaceTest 的方法进行默认实现,当子类需要使用适配器接口中的某个方法,而不是全部方法时,就可以通过继承抽象类,来完成对需要使用的特定方法重写操作即可,无需实现适配器接口里面的全部方法
public abstract class AbsAdapter implements InterfaceTest { //默认实现 public void m1() {} public void m2() {} public void m3() {} public void m4() {}}
Client 调用接口,重写适配器抽象类方法
public class Client { public static void main(String[] args) { AbsAdapter absAdapter = new AbsAdapter() { //只需要去覆盖我们 需要使用 接口方法 @Override public void m1() { System.out.println("使用了m1的方法"); } }; absAdapter.m1(); }}
一个顶层接口Power
public interface Power{ Integer getOutPut();}
分支一: 中国的220v电压
//中国的电压220Vpublic class ChinaPower implements Power{ private final Integer outPut=220; @Override public Integer getOutPut() { return outPut; }}
分支二: 日本的110v电压
//日本电压110vpublic class JapenPower implements Power{ private final Integer output=110; @Override public Integer getOutPut() { return output; }}
失配器接口–DC5Adapter
//适配器接口public interface DC5Adapter{ boolean support(Power power); Integer transTo5V(Power power);}
适配器类—ChinaAdapter–只负责将220v电压进行转换的工作
//该适配器负责将中国的220v电压转化为5v的电压public class ChinaAdapter implements DC5Adapter{ //当前适配器只负责将220v电压转化为5v的功能实现 private static Integer voltage=220; //判断当前适配器能否胜任传入power电压的转化职责 @Override public boolean support(Power power) { if(power.getOutPut().equals(voltage)) return true; return false; } //将220v电压转换为5v的 @Override public Integer transTo5V(Power power) { //获得被适配类,即我们需要将220v电压转化为5v返回 Integer output=power.getOutPut(); //进行电压转换操作 return output/44; }}
适配器类—JapenAdapter–只负责将110v电压进行转换的工作
//该适配器负责将日本的110v电压转化为5v的电压public class JapenAdapter implements DC5Adapter{ //当前适配器只负责将110v电压转化为5v的功能实现 private static Integer voltage=110; //判断是否支持将日本110v电压转化为5v电压的操作 @Override public boolean support(Power power) { if(power.getOutPut()==voltage) return true; return false; } //将110v电压转换为5v的 @Override public Integer transTo5V(Power power) { //获得被适配类,即我们需要将110v电压转化为5v返回 Integer output=power.getOutPut(); //进行电压转换操作 return output/22; }}
//手机需要5v的电压进行充电public class FindAdapter{ //存放所有适配器的set集合 private static final Set<DC5Adapter> DC5Adapters=new HashSet<>(); //通过静态代码块进行初始化操作 static { DC5Adapters.add(new ChinaAdapter()); DC5Adapters.add(new JapenAdapter()); } // 根据电压找合适的变压器 public DC5Adapter getPowerAdapter(Power power) { DC5Adapter dc5Adapter=null; for(DC5Adapter da:DC5Adapters) { //如果遍历到当前电压合适的变压器就直接退出遍历 if(da.support(power)) { dc5Adapter=da; break; } } //如果遍历完所有的变压器都没有找到合适的,就抛出异常 if(dc5Adapter==null) { throw new IllegalArgumentException("未能找到合适的变压器"); } //返回找到的合适的变压器 return dc5Adapter; }}
public class test{ @Test public void test() { //找寻合适的变压器是第一步 FindAdapter fa=new FindAdapter(); //找寻可以将220v转化为5v的变压器,即适配器 DC5Adapter adapter = fa.getPowerAdapter(new ChinaPower()); //输出当前变压器转化之后的电压 System.out.println(adapter.transTo5V(new ChinaPower())); }}
具体来说,类适配器模式还有如下优点:
SpringMVM 中的 HandlerAdapter(上图的第4步), 就使用了适配器模式;
Spring MVC中的适配器模式主要用于执行目标 Controller
中的请求处理方法。
在Spring MVC中,DispatcherServlet
作为用户,HandlerAdapter
作为期望接口(适配器接口),具体的适配器实现类用于对目标类进行适配,Controller
作为需要适配的类。
为什么要在 Spring MVC 中使用适配器模式?Spring MVC 中的 Controller
种类众多,不同类型的 Controller
通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet
直接获取对应类型的 Controller
,需要的自行来判断,像下面这段代码一样:
if(mappedHandler.getHandler() instanceof MultiActionController){ ((MultiActionController)mappedHandler.getHandler()).xxx }else if(mappedHandler.getHandler() instanceof XXX){ ... }else if(...){ ... }
这样假设如果我们增加一个 HardController
,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
,这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。
我们来看看源码,首先是适配器接口 HandlerAdapter
//适配器接口public interface HandlerAdapter { //判断当前的controller请求是否能被当前的适配器类处理 boolean supports(Object var1); //只有当支持处理当前请求后,才会执行下面的处理请求方法,返回一个ModelAndView对象 ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception; long getLastModified(HttpServletRequest var1, Object var2);}
现该接口的适配器每一个 Controller
都有一个适配器与之对应,这样的话,每自定义一个 Controller
需要定义一个实现 HandlerAdapter
的适配器。
springmvc 中提供的 Controller
实现类有如下:
springmvc 中提供的 HandlerAdapter
实现类如下
HttpRequestHandlerAdapter 这个适配器代码如下:
//不同的适配器类实现不同的功能//当前的HttpRequestHandlerAdapter 适配器类,只负责处理关于HttpRequest相关的请求public class HttpRequestHandlerAdapter implements HandlerAdapter { public HttpRequestHandlerAdapter() { } //判断当前的controller请求是否是HttpRequestHandler类型的 //当前适配器只支持处理当前类型的handler public boolean supports(Object handler) { return handler instanceof HttpRequestHandler; } //如果验证支持,会调用下面这个方法进行具体逻辑处理 public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //先进行强制类型转换,转换为指定的handler类型,然后就可以调用该类型处理对应请求的方法了 //调用HttpRequestHandler的handleRequest处理对应的请求 ((HttpRequestHandler)handler).handleRequest(request, response); return null; } public long getLastModified(HttpServletRequest request, Object handler) { return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L; }}
当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet
会通过 handler
的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的 hanle()
方法来调用 Controller
中的用于处理请求的方法。
public class DispatcherServlet extends FrameworkServlet {//用于存放所有HandlerAdapter适配器类的list集合 private List<HandlerAdapter> handlerAdapters; //初始化handlerAdapters private void initHandlerAdapters(ApplicationContext context) { //..省略... } // 遍历所有的 HandlerAdapters,通过 supports 判断找到匹配的适配器 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } } // 分发请求,请求需要找到匹配的适配器来处理 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request;
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/118905.html
摘要:原文地址设计模式十适配器模式在设计模式七设计模式分类中我们提到过结构设计模式,结构设计模式专注于设计对象和实例的构建组合过程。适配器模式在不修改现有代码的基础上,保留了架构。 原文地址:PHP设计模式(十):适配器模式 Introduction 在PHP设计模式(七):设计模式分类中我们提到过结构设计模式(Structural patterns),结构设计模式专注于设计对象(Objec...
摘要:本文参考于设计模式课程设计模式之适配器模式设计模式是一套被反复使用的多数人知晓的经过分类编目的代码设计经验的总结。第一个设计模式是适配器模式。总的来说适配器就是的模式,与修饰模式直接无感使用不同,适配器模式使用对象变为。 本文参考于:设计模式课程设计模式之适配器模式 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被...
摘要:与其它模式的异同适配器模式不会改变原有接口,这一点与装饰者模式和代理模式类似。代理模式适配器模式与代理模式最相似,同样都是创建一个新对象包装一次,实现对本体的调用。外观模式外观模式与适配器模式最大的区别,是定义了一个新的接口。 showImg(https://segmentfault.com/img/bVbul8d?w=800&h=600); 适配器模式:将一个类(对象)的接口(方法或...
摘要:适配器模式桥接模式过滤器模式组合模式装饰器模式外观模式享元模式代理模式行为型模式这些设计模式特别关注对象之间的通信。对象适配器另外一种适配器模式是对象适配器,它不是使用多继承或继承再实现的方式,而是使用直接关联,或者称为委托的方式。 设计模式汇总 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需...
阅读 2687·2021-10-12 10:12
阅读 2334·2021-09-02 15:41
阅读 2558·2019-08-30 15:55
阅读 1397·2019-08-30 13:05
阅读 2429·2019-08-29 11:21
阅读 3532·2019-08-28 17:53
阅读 3020·2019-08-26 13:39
阅读 798·2019-08-26 11:50