适配器模式- 2020-12-05 17:16- 设计模式: 适配器模式,设计模式


适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使 原本的接口不兼容的类可以一起工作,属于结构型设计模式。

适用场景

1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不 同厂家造成功能类似而接口不相同情况下的解决方案。

优点

1、能提高类的透明性和复用,现有的类复用但不需要改变。
2、目标类和适配器类解耦,提高程序的扩展性。
3、在很多业务场景中符合开闭原则

缺点

1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。
2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

demo

模拟电源适配

在中国民用电都是 220V 交流电,但我们手机使用的锂电池使用的 5V 直流电。因此,我 们给手机充电时就需要使用电源适配器来进行转换。

  1. /**
  2. * 220V电压
  3. *
  4. * @author Bai
  5. * @date 2020/12/5 16:12
  6. */
  7. public class AC220Service {
  8. /**
  9. * 输出220V电压
  10. *
  11. * @return
  12. */
  13. public int out220V() {
  14. System.out.println("输出220V电流");
  15. return 220;
  16. }
  17. }
  1. /**
  2. * 5V电流
  3. *
  4. * @author Bai
  5. * @date 2020/12/5 16:13
  6. */
  7. public interface DC5VService {
  8. /**
  9. * 输出5V
  10. *
  11. * @return
  12. */
  13. int out5V();
  14. }
  1. /**
  2. * 电流适配器:将220V转换为5V
  3. *
  4. * @author Bai
  5. * @date 2020/12/5 16:14
  6. */
  7. public class PowerAdapter implements DC5VService {
  8. @Override
  9. public int out5V() {
  10. AC220Service ac220Service = new AC220Service();
  11. //输出220V
  12. int ac220 = ac220Service.out220V();
  13. //转换5V
  14. int dc5v = ac220 / 44;
  15. System.out.println("输出5V:" + dc5v);
  16. //输出5V
  17. return dc5v;
  18. }
  19. }
  1. @Test
  2. public void demo() {
  3. DC5VService dc5VService = new PowerAdapter();
  4. dc5VService.out5V();
  5. }
  1. 输出220V电流
  2. 输出5V5

模拟用户登录

系统原有登录方式为用户名&密码,现在越来越多的登录方式也出现了,例如微信、QQ、手机号等,所以现有的登录方式已经不能满足业务需求了,在不改动原有逻辑的情况下,增加微信、QQ、手机号登录方式。采用 适配器+简单策略模式改造,策略模式其实还可以优化为每种登录方式作为一个Adapter。

  1. /**
  2. * 模拟用户根据用户名登录
  3. *
  4. * @author Bai
  5. * @date 2020/12/5 16:23
  6. */
  7. public class LogService {
  8. public boolean loginByName() {
  9. System.out.println("根据用户名登录");
  10. return true;
  11. }
  12. }
  1. /**
  2. * 登录适配器:遵循开闭原则,不改动原来登录的逻辑,新增微信、QQ、手机号登录方式
  3. * 适配模式+策略模式(简单的策略模式,复杂点可将每种登录方式封装为单独的Adapter)
  4. *
  5. * @author Bai
  6. * @date 2020/12/5 16:24
  7. */
  8. public class LoginAdapter {
  9. /**
  10. * 适配器+简单的策略模式
  11. *
  12. * @param loginType
  13. * @return
  14. */
  15. public boolean login(String loginType) {
  16. if ("weixin".equals(loginType)) {
  17. return weixinLogin();
  18. } else if ("QQ".equals(loginType)) {
  19. return qqLogin();
  20. } else if ("mobile".equals(loginType)) {
  21. return mobileLogin();
  22. } else if ("loginName".equals(loginType)) {
  23. LogService logService = new LogService();
  24. return logService.loginByName();
  25. }
  26. return false;
  27. }
  28. /**
  29. * 微信登录
  30. *
  31. * @return
  32. */
  33. public boolean weixinLogin() {
  34. System.out.println("微信登录");
  35. return true;
  36. }
  37. /**
  38. * QQ登录
  39. *
  40. * @return
  41. */
  42. public boolean qqLogin() {
  43. System.out.println("QQ登录");
  44. return true;
  45. }
  46. /**
  47. * 手机号登录
  48. *
  49. * @return
  50. */
  51. public boolean mobileLogin() {
  52. System.out.println("手机号登录");
  53. return true;
  54. }
  55. }
  1. @Test
  2. public void demo() {
  3. LoginAdapter loginAdapter = new LoginAdapter();
  4. loginAdapter.login("weixin");
  5. loginAdapter.login("QQ");
  6. loginAdapter.login("loginName");
  7. }

适配器模式在源码中的体现

SpringAOP 中的 AdvisorAdapter

SpringAOP 中的 AdvisorAdapter 类, 它有三个实现类 MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter 和
ThrowsAdviceAdapter,先来看顶层接口 AdvisorAdapter 的源代码:

  1. package org.springframework.aop.framework.adapter;
  2. import org.aopalliance.aop.Advice;
  3. import org.aopalliance.intercept.MethodInterceptor;
  4. import org.springframework.aop.Advisor;
  5. public interface AdvisorAdapter {
  6. boolean supportsAdvice(Advice var1);
  7. MethodInterceptor getInterceptor(Advisor var1);
  8. }

再看 MethodBeforeAdviceAdapter 类:

  1. package org.springframework.aop.framework.adapter;
  2. import java.io.Serializable;
  3. import org.aopalliance.aop.Advice;
  4. import org.aopalliance.intercept.MethodInterceptor;
  5. import org.springframework.aop.Advisor;
  6. import org.springframework.aop.MethodBeforeAdvice;
  7. class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
  8. MethodBeforeAdviceAdapter() { }
  9. public boolean supportsAdvice(Advice advice) {
  10. return advice instanceof MethodBeforeAdvice;
  11. }
  12. public MethodInterceptor getInterceptor(Advisor advisor) {
  13. MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
  14. return new MethodBeforeAdviceInterceptor(advice);
  15. }
  16. }

Spring 会根据不同的 AOP 配置来确定使用对 应的 Advice,跟策略模式不同的一个方法可以同时拥有多个 Advice

SpringMVC 中的 HandlerAdapter

其适配调用的关键代码还是在 DispatcherServlet 的 doDispatch()方法中,下面我们还是来看源码:

  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HttpServletRequest processedRequest = request;
  3. HandlerExecutionChain mappedHandler = null;
  4. boolean multipartRequestParsed = false;
  5. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  6. try {
  7. try {
  8. ModelAndView mv = null;
  9. Object dispatchException = null;
  10. try {
  11. processedRequest = this.checkMultipart(request);
  12. multipartRequestParsed = processedRequest != request;
  13. mappedHandler = this.getHandler(processedRequest);
  14. if(mappedHandler == null) {
  15. this.noHandlerFound(processedRequest, response);
  16. return;
  17. }
  18. HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
  19. String method = request.getMethod();
  20. boolean isGet = "GET".equals(method);
  21. if(isGet || "HEAD".equals(method)) {
  22. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  23. if(this.logger.isDebugEnabled()) {
  24. this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified)
  25. }
  26. if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
  27. return;
  28. }
  29. }
  30. if(!mappedHandler.applyPreHandle(processedRequest, response)) {
  31. return;
  32. }
  33. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  34. if(asyncManager.isConcurrentHandlingStarted()) {
  35. return;
  36. }
  37. this.applyDefaultViewName(processedRequest, mv);
  38. mappedHandler.applyPostHandle(processedRequest, response, mv);
  39. } catch (Exception var20) {
  40. dispatchException = var20;
  41. } catch (Throwable var21) {
  42. dispatchException = new NestedServletException("Handler dispatch failed", var21);
  43. }
  44. this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
  45. } catch (Exception var22) {
  46. this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
  47. } catch (Throwable var23) {
  48. this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
  49. }
  50. } finally {
  51. if(asyncManager.isConcurrentHandlingStarted()) {
  52. if(mappedHandler != null) {
  53. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  54. }
  55. } else if(multipartRequestParsed) {
  56. this.cleanupMultipart(processedRequest);
  57. }
  58. }
  59. }

在 doDispatch()方法中调用了 getHandlerAdapter()方法,来看代码:

  1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  2. if(this.handlerAdapters != null) {
  3. Iterator var2 = this.handlerAdapters.iterator();
  4. while(var2.hasNext()) {
  5. HandlerAdapter ha = (HandlerAdapter)var2.next();
  6. if(this.logger.isTraceEnabled()) {
  7. this.logger.trace("Testing handler adapter [" + ha + "]");
  8. }
  9. if(ha.supports(handler)) {
  10. return ha;
  11. }
  12. }
  13. }
  14. throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  15. }

在 getHandlerAdapter()方法中循环调用了 supports()方法判断是否兼容,循环迭代集 合中的 Adapter 又是在初始化时早已赋值。

参考资料

咕泡VIP架构师课程
**