适配器模式是最为广泛的设计模式之一,它不只普遍应用于代码开发,在平常生活里也很常见。好比笔记本上的电源适配器,可使用在110~ 220V之间变化的电源,而笔记本还能正常工做,这就是适配器模式最直接的例子,同时也是其思想的体现,简单的说,适配器模式就是把一个类(接口)转换成其余的类(接口)。前端
适配器模式,也叫包装模式,指的是**将一个类的接口变换成客户端所期待的另外一种接口,从而使本来因接口不匹配而没法在一块儿工做的两个类可以在一块儿工做。**咱们能够经过增长一个适配器类来解决接口不兼容的问题,而这个适配器类就至关于笔记本的适配器。后端
根据适配器类与适配者类的关系不一样,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。设计模式
Target(目标角色):该角色定义其余类转化成何种接口,能够是一个抽象类或接口,也能够是具体类。bash
Adaptee(源角色):你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象。app
Adapter(适配器角色):适配器模式的核心角色,职责就是经过继承或是类关联的方式把源角色转换为目标角色。框架
知道有哪些角色和UML类图后,咱们就能够写一下代码了,为了方便理解,咱们用生活中充电器的例子来说解适配器,如今有一个手机要充电,所须要的额定电压是5V,而家用交流电的电压是标准的220V,这种状况下要充电就须要有个适配器来作电压转换。async
把三者代入咱们上面画的类图不可贵出,充电器自己至关于Adapter,220V交流电至关于Adaptee,咱们的目标Target是5V直流电。ide
Target目标接口测试
public interface Voltage5 {
int output5V();
}
复制代码
目标接口的实现类ui
public class ConcreteVoltage5 implements Voltage5{
@Override
public int output5V() {
int src = 5;
System.out.println("目标电压:" + src + "V");
return src;
}
}
复制代码
Adaptee类
public class Voltage220 {
// 输出220V的电压
public int output220V() {
int src = 220;
System.out.println("源电压是:" + src + "V");
return src;
}
}
复制代码
Adapter类:完成220V-5V的转变
public class VoltageAdapter extends Voltage220 implements Voltage5 {
@Override
public int output5V() {
// 获取到源电压
int adaptee = super.output220V();
// 开始适配操做
System.out.println("对象适配器工做,开始适配电压");
int dst = adaptee / 44;
System.out.println("适配完成后输出电压:" + dst + "V");
return dst;
}
}
复制代码
经过适配器类的转换,咱们就能够把220V的电压转成咱们须要的5V电压了,写个场景类测试下:
/**
* 适配器模式
*/
public class Client {
public static void main(String[] args) {
Voltage5 voltage5 = new ConcreteVoltage5();
voltage5.output5V();
// 建立一个适配器对象
VoltageAdapter2 voltageAdapter = new VoltageAdapter2();
// 转换电压
voltageAdapter.output5V();
}
}
复制代码
结果输出
目标电压:5V
源电压是:220V
对象适配器工做,开始适配电压
适配完成后输出电压:5V
复制代码
前面说了,适配器模式分为两种,咱们上面介绍的经过继承的方式实现的是类适配器,还有一种对象适配器,它是经过关联适配器与适配者的方式实现的,它的通用类图以下所示:
下面对适配器模式作一下总结吧
类适配器:采用继承方式,对Java这种不支持多继承的语言来讲,一次只能适配一个适配器类,不太方便
对象适配器:与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。若是必定要置换掉适配者类的一个或多个方法,能够先作一个适配者类的子类,将适配者类的方法置换掉,而后再把适配者类的子类当作真正的适配者进行适配,实现过程较为复杂。
这里扩展一下适配器模式的知识点,想必作过Java开发的都知道SpringMVC,这套框架能够帮助咱们把前端的请求访问到后台对应的controller的方法上,而后再把处理结果返回给后端,它的底层其实就用到了适配器模式。
SpringMVC中的适配器模式主要用于执行目标Controller中的请求处理方法。在它的底层处理中,DispatcherServlet做为用户,HandlerAdapter做为指望接口,具体的适配器实现类用于对目标类进行适配,Controller做为须要适配的类。
为何要在 Spring MVC 中使用适配器模式?Spring MVC 中的 Controller 种类众多,不一样类型的 Controller 经过不一样的方法来对请求进行处理。若是不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,那样每增长一个类型的Controller就须要使用增长一个if else判断instance of,这违反了设计模式的开闭原则 —— 对扩展开放,对修改关闭。
那么SpringMVC是怎么处理的呢?咱们来简单看一下源码,首先是适配器接口HandlerAdapter,
public interface HandlerAdapter {
boolean supports(Object var1);
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
复制代码
该接口的适配器类对应着 Controller,每自定义一个Controller须要定义一个实现HandlerAdapter的适配器。举个例子,有一个适配器类HttpRequestHandlerAdapter
,该类就是实现了HandlerAdapter接口,这是它的源码:
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
复制代码
当Spring容器启动后,会将全部定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet会经过handler的类型找到对应适配器,并将该适配器对象返回给用户,而后就能够统一经过适配器的handle
方法来调用Controller中的用于处理请求的方法。
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//.........................
//找到匹配当前请求对应的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
}
// 遍历集合,找到合适的匹配器
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;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
}
复制代码
这样一来,全部的controller就都统一交给HandlerAdapter处理,免去了大量的 if-else 语句判断,同时增长controller类型只需增长一个适配器便可,不须要修改到Servlet的逻辑,符合开闭原则。
关于适配器模式的介绍就到这里了,有不足之处还望指出。
《设计模式之禅》