适配器模式详解

适配器模式

现实生活中的适配器例子

泰国插座用的是两孔的(欧标),可以买个多功能转换插头(适配器),这样就可以使用了。

基本介绍

  1. 适配器模式 (Adapter Pattern) 将某个类的接口转换成客户端期望的另一个接口表示 ,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器 (Wrapper)
  2. 适配器模式属于结构型模式
  3. 主要分为三类:类适配器模式接口适配器模式对象适配器模式

工作原理

  1. 适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容
  2. 从用户的角度看不到被适配者,是解耦的
  3. 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
  4. 用户收到反馈结果,感觉只是和目标接口交互,如图

类适配器模式

基本介绍:Adapter 类,通过继承 src 类,实现 dst 类接口,完成 src -> dst 的适配。

类适配器模式应用实例

应用实例说明:

  • 以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src(即被适配者),我们的 dst(即目标)是 5V 直流电

代码实现

客户端(Client)
java 复制代码
public class Client {

    public static void main(String[] args) {
        System.out.println(" === 类适配器模式 ====");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }

}
实体类(Phone)
java 复制代码
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, 不能充电~~");
        }
    }
}
适配接口(IVoltage5V)
java 复制代码
public interface IVoltage5V {

    int output5V();
}
适配器类(VoltageAdapter)
java 复制代码
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

	@Override
	public int output5V() {

		// 获取到 220V 电压
		int srcV = output220V();

		// 转成 5v
		return srcV / 44;
	}

}
被适配的类(Voltage220V)
java 复制代码
public class Voltage220V {

    // 输出220V的电压
    public int output220V() {
        int src = 220;
        System.out.println("电压=" + src + "伏");
        return src;
    }
}

时序图

类适配器模式注意事项和细节

  1. Java 是单继承机制,所以 类适配器 需要继承 src 类这一点算是一个缺点,因为这要求 dst 必须是接口,有一定局限性

  2. src 类的方法在 Adapter 中都会暴露出来,也增加了使用的成本。

  3. 由于其继承了 src 类,所以它可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强了。

类适配器:VoltageAdapter

src:Voltage220V

dst:IVoltage5V

接口适配器模式

基本介绍

  1. 一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式
  2. 核心思路:当不需要全部实现接口提供的方法时,可先设计一个 抽象类 实现 接口,并为该接口中每个方法提供一个默认实现(空方法) ,那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
  3. 适用于一个接口不想使用其所有的方法的情况。
接口(Interface4)
java 复制代码
public interface Interface4 {

	void m1();

	void m2();

	void m3();

	void m4();
}
抽象类(AbsAdapter)
java 复制代码
/**
 * 在 AbsAdapter 我们将 Interface4 的方法进行默认实现
 *
 * @author chenmeng
 */
public abstract class AbsAdapter implements Interface4 {

	// 默认实现
	@Override
    public void m1() {

	}

	@Override
	public void m2() {

	}

	@Override
	public void m3() {

	}

	@Override
	public void m4() {

	}
}
客户端(Client)
java 复制代码
public class Client {
	public static void main(String[] args) {
		
		AbsAdapter absAdapter = new AbsAdapter() {
			// 只需要去覆盖我们 需要使用的 接口方法
			@Override
			public void m1() {
				System.out.println("使用了m1的方法");
			}
		};
		
		absAdapter.m1();
	}

}

时序图

对象适配器模式

基本介绍

  1. 基本思路和类的适配器模式相同,只是将 Adapter 类作修改,不是继承 src 类,而是持有 src 类的实例,以解决兼容性的问题。即:持有 src 类,实现 dst 类接口,完成 src -> dst 的适配
  2. 根据"合成复用原则 ",在系统中尽量使用关联关系(聚合)来替代继承关系
  3. 对象适配器模式是适配器模式常用的一种

应用实例

以生活中充电器的例子来讲解适配器,充电器本身 相当于 Adapter220V 交流电 相当于 src(即被适配者),我们的 dst(即目标)是 5V 直流电,使用对象适配器模式完成。

代码实现

对比 类适配器模式 ,只有 适配器类 进行了修改,使用关联关系(聚合)来替代继承关系

客户端(Client)
java 复制代码
public class Client {

	public static void main(String[] args) {
		System.out.println(" === 对象适配器模式 ====");
		Phone phone = new Phone();
		phone.charging(new VoltageAdapter(new Voltage220V()));
	}

}
实体类(Phone)
java 复制代码
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, 不能充电~~");
		}
	}
}
适配接口(IVoltage5V)
java 复制代码
public interface IVoltage5V {

    int output5V();
}
适配器类(VoltageAdapter)
java 复制代码
public class VoltageAdapter implements IVoltage5V {

    // 关联关系-聚合
    private final Voltage220V voltage220V;

    // 通过构造器,传入一个 Voltage220V 实例
    public VoltageAdapter(Voltage220V voltage220v) {
        this.voltage220V = voltage220v;
    }

    @Override
    public int output5V() {
        int dst = 0;

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

        return dst;
    }
}
被适配的类(Voltage220V)
java 复制代码
public class Voltage220V {

    // 输出220V的电压
    public int output220V() {
        int src = 220;
        System.out.println("电压=" + src + "伏");
        return src;
    }
}

时序图

对象适配器模式注意事项和细节

  1. 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。
  2. 根据合成复用原则,使用组合替代继承 ,所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst 必须是接口
  3. 使用成本更低,更灵活。

适配器模式在 SpringMVC 框架应用的源码剖析

  1. SpringMVC 中的 HandlerAdapter, 就使用了适配器模式

  2. SpringMVC 处理请求的流程回顾

    • Spring 定义了一个适配接口,使得每一种 Controller 有一种对应的适配器实现类
    • 适配器代替 Controller 执行相应的方法
    • 扩展 Controller 时,只需要增加一个适配器类就完成了 SpringMVC 的扩展了,
  3. 使用 HandlerAdapter 的原因分析:

    • 可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,
    • 如果需要直接调用 Controller 方法,需要调用的时候就得不断是使用 if else 来进行判断是哪一种子类然后执行。
    • 那么如果后面要扩展 Controller,就得修改原来的代码,这样违背了 OCP 开闭原则

SpringMVC 的 HandlerController接口,HttpRequestHandler,Servlet、@RequestMapping )有四种表现形式,在 Handler 不确定是什么方式的时候(可能是方法、也可能是类),适配器这种设计模式就能模糊掉具体的实现,从而就能提供统一访问接口。

什么是 HandlerAdapter

  • 在 SpringMVC 中,HandlerAdapter 是一个接口,它的作用是将请求的 Handler(控制器)适配到 SpringMVC 的框架中。
  • 由于 SpringMVC 支持多种类型的控制器,包括基于注解的控制器、基于接口的控制器等,每种控制器的调用方式都可能不同。
  • HandlerAdapter提供了一个统一的接口,使得 SpringMVC 可以以相同的方式调用不同类型的控制器。

HandlerAdapter 的主要方法:

  • supports(Object handler):判断适配器是否支持给定的 Handler。
  • handle(HttpServletRequest request, HttpServletResponse response, Object handler):调用 Handler 处理请求。
  • getLastModified(HttpServletRequest request, Object handler):获取请求的最新修改时间。

适配器模式的应用

  1. SpringMVC 通过 HandlerAdapter 接口,为每种类型的控制器提供了一个 适配器实现类
  2. 这样,当 DispatcherServlet 接收到请求时,它会根据请求找到相应的 Handler,然后通过 getHandlerAdapter 方法获取对应的 HandlerAdapter
  3. 最后通过适配器调用控制器的方法。

HandlerAdapter 的实现子类:

HandlerAdapter 的实现子类使得每一种 Controller 都有一种对应的适配器实现类,每种 Controller 有不同的实现方式

  1. AbstractHandlerMethodAdapter:

    • 这是一个抽象类,提供了一些基础的实现,用于简化具体 HandlerAdapter 的实现。它并不是直接用于处理请求,而是作为其他适配器的基类
  2. HandlerFunctionAdapter:

    • 这个适配器用于适配基于 Java 8 函数式接口的处理器,例如 java.util.function.BiFunction
  3. HttpRequestHandlerAdapter:

    • 用于适配实现了 HttpRequestHandler 接口的处理器。这种处理器比较少见,通常用于一些特殊的场景。
  4. RequestMappingHandlerAdapter:

    • 继承 AbstractHandlerMethodAdapter

    • 这是 Spring MVC 中最常用 的适配器之一,用于适配带有 @RequestMapping 注解的控制器方法。它支持各种 HTTP 方法的映射,如 GET、POST 等。

  5. SimpleControllerHandlerAdapter:

    • 用于适配实现了Controller接口的处理器。这种适配器在 Spring MVC 中使用较少,因为 @Controller 注解更为常用。
  6. SimpleServletHandlerAdapter:

    • 用于适配实现了 Servlet 接口的处理器。这允许在 Spring MVC 中使用传统的 Servlet 作为控制器。

图示:

源码分析

调度类(DispatcherServlet)
java 复制代码
public class DispatcherServlet extends FrameworkServlet {
    // ...
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    // 通过 HandlerMapping 来映射 Controller
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    // 获取适配器
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    // 通过适配器调用 Controller 的方法并返回 ModelAndView
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    Exception ex = var20;
                    dispatchException = ex;
                } catch (Throwable var21) {
                    Throwable err = var21;
                    dispatchException = new NestedServletException("Handler dispatch failed", err);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                Exception ex = var22;
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
            } catch (Throwable var23) {
                Throwable err = var23;
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }
    
    @Nullable
    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 void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (pageNotFoundLogger.isWarnEnabled()) {
            pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
        }

        if (this.throwExceptionIfNoHandlerFound) {
            throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), (new ServletServerHttpRequest(request)).getHeaders());
        } else {
            response.sendError(404);
        }
    }

    // 根据需要,返回适当的 HandlerAdapter
    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)
java 复制代码
public interface HandlerAdapter {

    /**
     * 判断当前的HandlerAdapter是否支持给定的handler。
     * <p>这是确定是否使用当前适配器处理请求的关键方法。
     * 每个适配器通常只能适配一种特定类型的处理器,
     * 因此此方法用于检查给定的handler是否与适配器兼容。
     *
     * @param handler 需要处理的处理器对象
     * @return 如果适配器支持该handler,则返回true;否则返回false。
     */
    boolean supports(Object handler);

    /**
     * 核心方法:利用Handler处理请求,然后返回一个ModelAndView。
     * <p>DispatcherServlet最终就是调用此方法,来返回一个ModelAndView的。
     * 这个方法将请求委托给具体的handler处理,并接收处理结果,
     * 通常包括视图信息和模型数据。
     *
     * @param request 当前的HttpServletRequest对象,包含请求数据。
     * @param response 当前的HttpServletResponse对象,用于构造响应。
     * @param handler 需要处理请求的Handler对象。
     * @return ModelAndView对象,包含视图信息和模型数据。
     * @throws Exception 如果处理过程中发生异常,将被抛出。
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * 同HttpServlet的getLastModified方法。
     * <p>用于确定请求资源的最后修改时间,以便进行缓存控制。
     * 如果处理器不支持最后修改时间的确定,此方法可以返回-1。
     * <p>注意:此方法已被弃用,建议使用其他机制实现缓存控制。
     *
     * @param request 当前的HttpServletRequest对象,包含请求数据。
     * @param handler 需要处理请求的Handler对象。
     * @return 资源的最后修改时间戳,或者-1表示不支持。
     * @deprecated 由于缓存控制的最佳实践已经变化,此方法不再推荐使用。
     */
    @Deprecated
    long getLastModified(HttpServletRequest request, Object handler);
}
适配器类(SimpleControllerHandlerAdapter)
java 复制代码
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    public SimpleControllerHandlerAdapter() {
    }

    public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((Controller)handler).handleRequest(request, response);
    }

    public long getLastModified(HttpServletRequest request, Object handler) {
        return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
    }
}

模拟手写 SpringMVC

调度类(DispatcherServlet)
java 复制代码
public class DispatcherServlet {

	public static List<HandlerAdapter> handlerAdapters = new ArrayList<>();

	public DispatcherServlet() {
		handlerAdapters.add(new AnnotationHandlerAdapter());
		handlerAdapters.add(new HttpHandlerAdapter());
		handlerAdapters.add(new SimpleHandlerAdapter());
	}

	public void doDispatch() {
		// 此处模拟SpringMVC从request取handler的对象,
		// 适配器可以获取到希望的Controller
		 HttpController controller = new HttpController();
		// AnnotationController controller = new AnnotationController();
		//SimpleController controller = new SimpleController();
		// 得到对应适配器
		HandlerAdapter adapter = getHandler(controller);
		// 通过适配器执行对应的controller对应方法
		adapter.handle(controller);

	}

	public HandlerAdapter getHandler(Controller controller) {
		//遍历:根据得到的controller(handler), 返回对应适配器
		for (HandlerAdapter adapter : handlerAdapters) {
			if (adapter.supports(controller)) {
				return adapter;
			}
		}
		return null;
	}

	public static void main(String[] args) {
		new DispatcherServlet().doDispatch(); // http...
	}

}
适配接口(HandlerAdapter)
java 复制代码
public interface HandlerAdapter {

    boolean supports(Object handler);

    void handle(Object handler);
}

// 多种适配器类
class SimpleHandlerAdapter implements HandlerAdapter {

    @Override
    public void handle(Object handler) {
       ((SimpleController) handler).doSimplerHandler();
    }

    @Override
    public boolean supports(Object handler) {
       return (handler instanceof SimpleController);
    }

}

//
class HttpHandlerAdapter implements HandlerAdapter {

    @Override
    public void handle(Object handler) {
       ((HttpController) handler).doHttpHandler();
    }

    @Override
    public boolean supports(Object handler) {
       return (handler instanceof HttpController);
    }

}

//
class AnnotationHandlerAdapter implements HandlerAdapter {

    @Override
    public void handle(Object handler) {
       ((AnnotationController) handler).doAnnotationHandler();
    }

    @Override
    public boolean supports(Object handler) {

       return (handler instanceof AnnotationController);
    }
}
多种 Controller 实现
java 复制代码
public interface Controller {

}

//
class HttpController implements Controller {
    public void doHttpHandler() {
       System.out.println("http...");
    }
}

//
class SimpleController implements Controller {
    public void doSimplerHandler() {
       System.out.println("simple...");
    }
}

//
class AnnotationController implements Controller {
    public void doAnnotationHandler() {
       System.out.println("annotation...");
    }
}

适配器模式的注意事项和细节

  1. 三种命名方式,是根据 src 是以怎样的形式给到 Adapter(在Adapter里的形式)来命名的。

    • 类适配器:以类给到,在 Adapter 里,就是将 src 当做类,继承

    • 对象适配器:以对象给到,在 Adapter 里,将 src 作为一个对象,持有

    • 接口适配器:以接口给到,在 Adapter 里,将 src 作为一个接口,实现

  2. Adapter 模式最大的作用还是将原本不兼容的接口融合在一起工作

  3. 实际开发中,实现起来不拘泥于我们讲解的三种经典形式

学习参考

相关推荐
wenbin_java3 小时前
设计模式之解释器模式:原理、实现与应用
java·设计模式·解释器模式
-权子-5 小时前
设计模式 - 代理模式Proxy
设计模式·代理模式
StrongerLLL6 小时前
策略模式
设计模式
01空间7 小时前
设计模式简述(二)单例模式
设计模式
渊渟岳8 小时前
为什么学习设计模式?
设计模式
程序员JerrySUN13 小时前
设计模式 Day 3:抽象工厂模式(Abstract Factory Pattern)详解
设计模式·抽象工厂模式
程序员沉梦听雨16 小时前
原型模式详解
设计模式·原型模式
coderzpw17 小时前
谁该处理我的请假?——责任链模式
设计模式·责任链模式
云徒川18 小时前
【设计模式】组合模式
设计模式·组合模式