适配器模式- 2020-12-05 17:16- 设计模式: 适配器模式,设计模式
适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使 原本的接口不兼容的类可以一起工作,属于结构型设计模式。
适用场景
1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不 同厂家造成功能类似而接口不相同情况下的解决方案。
优点
1、能提高类的透明性和复用,现有的类复用但不需要改变。
2、目标类和适配器类解耦,提高程序的扩展性。
3、在很多业务场景中符合开闭原则。
缺点
1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。
2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
demo
模拟电源适配
在中国民用电都是 220V 交流电,但我们手机使用的锂电池使用的 5V 直流电。因此,我 们给手机充电时就需要使用电源适配器来进行转换。
/*** 220V电压** @author Bai* @date 2020/12/5 16:12*/public class AC220Service {/*** 输出220V电压** @return*/public int out220V() {System.out.println("输出220V电流");return 220;}}
/*** 5V电流** @author Bai* @date 2020/12/5 16:13*/public interface DC5VService {/*** 输出5V** @return*/int out5V();}
/*** 电流适配器:将220V转换为5V** @author Bai* @date 2020/12/5 16:14*/public class PowerAdapter implements DC5VService {@Overridepublic int out5V() {AC220Service ac220Service = new AC220Service();//输出220Vint ac220 = ac220Service.out220V();//转换5Vint dc5v = ac220 / 44;System.out.println("输出5V:" + dc5v);//输出5Vreturn dc5v;}}
@Testpublic void demo() {DC5VService dc5VService = new PowerAdapter();dc5VService.out5V();}
输出220V电流输出5V:5
模拟用户登录
系统原有登录方式为用户名&密码,现在越来越多的登录方式也出现了,例如微信、QQ、手机号等,所以现有的登录方式已经不能满足业务需求了,在不改动原有逻辑的情况下,增加微信、QQ、手机号登录方式。采用 适配器+简单策略模式改造,策略模式其实还可以优化为每种登录方式作为一个Adapter。
/*** 模拟用户根据用户名登录** @author Bai* @date 2020/12/5 16:23*/public class LogService {public boolean loginByName() {System.out.println("根据用户名登录");return true;}}
/*** 登录适配器:遵循开闭原则,不改动原来登录的逻辑,新增微信、QQ、手机号登录方式* 适配模式+策略模式(简单的策略模式,复杂点可将每种登录方式封装为单独的Adapter)** @author Bai* @date 2020/12/5 16:24*/public class LoginAdapter {/*** 适配器+简单的策略模式** @param loginType* @return*/public boolean login(String loginType) {if ("weixin".equals(loginType)) {return weixinLogin();} else if ("QQ".equals(loginType)) {return qqLogin();} else if ("mobile".equals(loginType)) {return mobileLogin();} else if ("loginName".equals(loginType)) {LogService logService = new LogService();return logService.loginByName();}return false;}/*** 微信登录** @return*/public boolean weixinLogin() {System.out.println("微信登录");return true;}/*** QQ登录** @return*/public boolean qqLogin() {System.out.println("QQ登录");return true;}/*** 手机号登录** @return*/public boolean mobileLogin() {System.out.println("手机号登录");return true;}}
@Testpublic void demo() {LoginAdapter loginAdapter = new LoginAdapter();loginAdapter.login("weixin");loginAdapter.login("QQ");loginAdapter.login("loginName");}
适配器模式在源码中的体现
SpringAOP 中的 AdvisorAdapter
SpringAOP 中的 AdvisorAdapter 类, 它有三个实现类 MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter 和
ThrowsAdviceAdapter,先来看顶层接口 AdvisorAdapter 的源代码:
package org.springframework.aop.framework.adapter;import org.aopalliance.aop.Advice;import org.aopalliance.intercept.MethodInterceptor;import org.springframework.aop.Advisor;public interface AdvisorAdapter {boolean supportsAdvice(Advice var1);MethodInterceptor getInterceptor(Advisor var1);}
再看 MethodBeforeAdviceAdapter 类:
package org.springframework.aop.framework.adapter;import java.io.Serializable;import org.aopalliance.aop.Advice;import org.aopalliance.intercept.MethodInterceptor;import org.springframework.aop.Advisor;import org.springframework.aop.MethodBeforeAdvice;class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {MethodBeforeAdviceAdapter() { }public boolean supportsAdvice(Advice advice) {return advice instanceof MethodBeforeAdvice;}public MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();return new MethodBeforeAdviceInterceptor(advice);}}
Spring 会根据不同的 AOP 配置来确定使用对 应的 Advice,跟策略模式不同的一个方法可以同时拥有多个 Advice
SpringMVC 中的 HandlerAdapter
其适配调用的关键代码还是在 DispatcherServlet 的 doDispatch()方法中,下面我们还是来看源码:
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;Object dispatchException = null;try {processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;mappedHandler = this.getHandler(processedRequest);if(mappedHandler == null) {this.noHandlerFound(processedRequest, response);return;}HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = "GET".equals(method);if(isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if(this.logger.isDebugEnabled()) {this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified)}if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {return;}}if(!mappedHandler.applyPreHandle(processedRequest, response)) {return;}mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if(asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {dispatchException = new NestedServletException("Handler dispatch failed", var21);}this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));}} finally {if(asyncManager.isConcurrentHandlingStarted()) {if(mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if(multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}}
在 doDispatch()方法中调用了 getHandlerAdapter()方法,来看代码:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if(this.handlerAdapters != null) {Iterator var2 = this.handlerAdapters.iterator();while(var2.hasNext()) {HandlerAdapter ha = (HandlerAdapter)var2.next();if(this.logger.isTraceEnabled()) {this.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");}
在 getHandlerAdapter()方法中循环调用了 supports()方法判断是否兼容,循环迭代集 合中的 Adapter 又是在初始化时早已赋值。
参考资料
咕泡VIP架构师课程
**
