适配器模式做的就是,有一个接口需要实现,但是我们现成的对象都不满足,需要加一层适配器来进行适配。适配器模式总体来说分三种:默认适配器模式、对象适配器模式、类适配器模式。那我们生活中很常见的一个例子来讲,比如你水龙头接口是4cm,而房间室内接入管道是10cm此时我们没法直接对接,就需要一个中间件来联通水龙头与管道这个过程就叫适配,水暖上名字称为大小头咳咳跑远了。

默认适配器模式:

首先,我们先看看最简单的适配器模式默认适配器模式(Default Adapter)是怎么样的。我们用 Appache commons-io 包中的 FileAlteration Listener 做例子,此接口定义了很多的方法,用于对文件或文件夹进行监控,一旦发生了对应的操作,就会触发相应的方法。

public interface FileAlterationListener {void onStart(final FileAlterationObserver observer);void onDirectoryCreate(final File directory);void onDirectoryChange(final File directory);void onDirectoryDelete(final File directory);void onFileCreate(final File file);void onFileChange(final File file);void onFileDelete(final File file);void onStop(final FileAlterationObserver observer);
}

此接口的一大问题是抽象方法太多了,如果我们要用这个接口,意味着我们要实现每一个抽象方法,如果我们只是想要监控文件夹中的文件创建和文件删除事件,可是我们还是不得不实现所有的方法,很明显,这不是我们想要的。所以,我们需要下面的一个适配器,它用于实现上面的接口,但是所有的方法都是空方法。这样,我们就可以转而定义自己的类来继承下面这个类即可。

public class FileAlterationListenerAdaptor implements FileAlterationListener {public void onStart(final FileAlterationObserver observer) {}public void onDirectoryCreate(final File directory) {}public void onDirectoryChange(final File directory) {}public void onDirectoryDelete(final File directory) {}public void onFileCreate(final File file) {}public void onFileChange(final File file) {}public void onFileDelete(final File file) {}public void onStop(final FileAlterationObserver observer) {}
}

比如我们可以定义以下类,我们仅仅需要实现我们想实现的方法就可以了:

public class FileMonitor extends FileAlterationListenerAdaptor {public void onFileCreate(final File file) {// 文件创建doSomething();}public void onFileDelete(final File file) {// 文件删除doSomething();}
}

当然,上面只是适配器模式的其中一种,也是最简单的一种平时工作中基本不会使用这种形式。

对象适配器模式:

public interface Duck {public void quack(); // 鸭的呱呱叫public void fly(); // 飞
}public interface Cock {public void gobble(); // 鸡的咕咕叫public void fly(); // 飞
}public class WildCock implements Cock {public void gobble() {System.out.println("咕咕叫");}public void fly() {System.out.println("鸡也会飞哦");}
}

鸭接口有 fly() 和 quack() 两个方法,鸡 Cock 如果要冒充鸭,fly() 方法是现成的,但是鸡不会鸭的呱呱叫,没有 quack() 方法。这个时候就需要适配了:

// 毫无疑问,首先,这个适配器肯定需要 implements Duck,这样才能当做鸭来用
public class CockAdapter implements Duck {Cock cock;// 构造方法中需要一个鸡的实例,此类就是将这只鸡适配成鸭来用public CockAdapter(Cock cock) {this.cock = cock;}// 实现鸭的呱呱叫方法@Overridepublic void quack() {// 内部其实是一只鸡的咕咕叫cock.gobble();}@Overridepublic void fly() {cock.fly();}
}

类适配器模式:

HanlderAdapter真的采用了适配器模式么?

spring中大量使用到了适配器这种设计模式,就拿最经典的springMvc中的九大组件之一的 HanlderAdapter,从名字看就明白了这里十有八九用到了适配器模式但是叫Adapter就真的采用了适配器模式么??????看到百分之九十九的文章都打着适配器模式的幌子,但是本人并不认同:

  • springmvc通过HandlerMapping获取到可以处理的handler,这些handler的类型各不相同,对请求的预处理,参数获取都不相同,最简单的做法是根据不同的handler类型,做一个分支处理,不同的handler编写不同的代码。这样的问题是很明显的,分支判断复杂,代码庞大,不符合单一职责原则。如果要增加一种handler类型,需要修改代码增加分支处理,违反了开闭原DispatcherServelt与多个handler发生了交互,违反迪米特法则。而使用适配器模式,就可以很好的解决这个问题。
  • 不直接对handler进行处理,而是将handler交给适配器HandlerAdapter去处理,这样DispatcherServlet交互的类就只剩下一个接口,Handl erAdapter,符合迪米特法则,尽可能少的与其他类发生交互,将handler交给HandlerAdapter处理后,不同类型的handler被对应类型的Han dlerAdapter处理,每个HandlerAdapter都只完成单一的handler处理,符合单一职责原则。
public interface HandlerAdapter {/*** 判断是否可以使用某个handler*/boolean supports(Object handler);/*** 具体使用handler干活*/@NullableModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;/*** 获取资源最后一次修改的时间戳*/long getLastModified(HttpServletRequest request, Object handler);}

adapter的作用就是当请求过来时,使用实现了controller接口的的处理器来干活。干活的方法是直接调用处理器的handleRequest ( HttpSer vletRequest request, HttpServletResponse response)方法。这里我们可以看出对应不同的请求supports和getLastModified两个方法都是一样的。但是handle处理方法却要因请求的方式不同而异。HttpRequestHandlerAdapter、SimpleServletHandlerAdapter和Simple Controller Adapter分别适配了HttpRequestHandler、Servlet和Controller类型的Handler方法非常简单都是调用Handler里的固定方法。选择哪个Handl erAdapter的过程在getHandlerAdapter方法中,它的逻辑是遍历所有的Adapter,然后检查哪个可以处理当前的handler,找到第一个可以处理Handler的Adapter后停止查找并将其返回。HttpRequestHandlerAdapter

DispatcherServlet中的doDispatch方法:

 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {。。。。。。。。。。。。。。。try {// 检查请求有没有携带二进制文件,如何有则需要在http请求头中进行申明Multipart-form-dataprocessedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request. 确定当前请求的处理程序// 1.获得当前请求的handler,在通常情况下其实就是controller对象但不完全一定是mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// getHandler方法主要会调用已经注册好了的handlerMapping中的getHandler方法// Determine handler adapter for the current request.// 2.根据找到的handler找到对应的handlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}

这样假设如果我们增加一个HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。 因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类, 让适配器代替controller执行相应的方法。这样在扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return (handler instanceof Controller);}@Override@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {/*** {@link AbstractController#handleRequest(HttpServletRequest, HttpServletResponse)}*/return ((Controller) handler).handleRequest(request, response);}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {if (handler instanceof LastModified) {return ((LastModified) handler).getLastModified(request);}return -1L;}}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {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");}

可以明显的看到在获取适配器时是通过不断遍历,调用supports方法使用instanceof 关键字来进行判断的。如果符合则返回对应适配器类。

SimpleServletHandlerAdapter:
@Overridepublic boolean supports(Object handler) {return (handler instanceof Servlet);}
SimpleControllerHandlerAdapter:
@Overridepublic boolean supports(Object handler) {return (handler instanceof Controller);}
HttpRequestHandlerAdapter:
@Overridepublic boolean supports(Object handler) {return (handler instanceof HttpRequestHandler);}

到这里我认为,HandlerAdapter 本身和适配器设计模式并不沾边,不过设计模式本身也只是一种思想并不固定。

设计模式---适配器模式(springMvc中HandlerAdapter 的误区)相关推荐

  1. java设计模式适配器模式_Java中的适配器设计模式

    java设计模式适配器模式 适配器设计模式是一种结构设计模式 ,可以帮助我们连接到通过不同接口公开相似功能的旧版或第三方代码. 适配器的现实世界是我们用来将USB电缆连接到以太网端口的类比. 在设计一 ...

  2. springmvc中的设计模式---适配器模式_晏无心_新浪博客

    springmvc的基本流程在之前的文章里都大致分析完了,接下来总结下springmvc中运用了哪些设计模式. 一.设计模式 什么是设计模式,通俗来讲,就是是一套被反复使用.多数人知晓的.经过分类的. ...

  3. 9种设计模式在Spring中的运用,一定要非常熟练!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:iCoding91 https://blog.csdn.ne ...

  4. 简述事件接口与事件适配器的联系与区别_设计模式——适配器模式

    适配器模式 适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper).适配器模式既可以作为类结构型模式,也 ...

  5. 设计模式 | 适配器模式及典型应用

    前言 本文介绍适配器模式,源码分析spring aop, jpa, mvc中的适配器模式 推荐阅读 设计模式 | 简单工厂模式及典型应用 设计模式 | 工厂方法模式及典型应用 设计模式 | 抽象工厂模 ...

  6. 九种设计模式在Spring中的应用

    Spring中涉及的设计模式总结 1.简单工厂(非23种设计模式中的一种) 实现方式: BeanFactory.Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获 ...

  7. 软件设计模式——适配器模式

    摘要 现实生活中的适配器例子,泰国插座用的是两孔的(欧标),可以买个多功能转换插头(适配器),这样就可以使用了. 1)适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个 ...

  8. java设计模式在项目中的使用_SpringMVC项目里,有必要使用一些设计模式吗?

    背景 为了兼容多种业务,想在项目中使用一些设计模式,以便于管理. 例如,我需要创建用户并返回userid,每种业务创建的方式都不一样. 我选取了"适配器模式",但是我发现,在spr ...

  9. SpringMVC中使用@RequestBody,@ResponseBody注解实现Java对象和XML/JSON数据自动转换)

    Spring3.1开始使用新的HandlerMapping 和 HandlerAdapter 来支持@Contoller 和@RequestMapping注解处理:处理器映射RequestMappin ...

最新文章

  1. GridView使用的技巧
  2. Sublime Text3配置Lua运行环境
  3. Silverlight实用窍门系列:22.Silverlight使用WebService调用C++,Delphi编写的DLL文件【实例源码下载】...
  4. Android 获取当前日期距离过期时间的日期差值的完整方法直接使用
  5. java前后端用json传值_前后端——json的传值与接收(springMvc)
  6. HTML小知识点积累
  7. 工业以太网交换机的重要技术参数分析
  8. 拳王虚拟项目公社:分享一个大学生上班族都可做的虚拟副业项目
  9. python并行计算for循环_在python中并行化这个嵌套的for循环
  10. 下载LineageOS 源码编译
  11. android ndk 架构,NDK需要特别注意的armeabi等架构问题
  12. 2014计算机三级网络,2014计算机三级网络技术知识点.doc
  13. 卡内基梅隆大学计算机硕士专业,2020年卡内基梅隆大学专业设置
  14. PAAS平台(摘360百科)
  15. 简历制作 | 保研 | 考研复试
  16. 电脑插入U盘后显示CD驱动器,如何还原为正常U盘?
  17. 计算机与资源管理器有何区别,资源管理器与我的电脑有什么不同.PPT
  18. 计算机专业要考什么证书?
  19. k8s面试中最常见的50个问题(翻译)
  20. 股票量化对冲策略的发展与展望

热门文章

  1. 2022年无人值守煤炭运销管理系统性能有什么要求
  2. Nginx 代理配置
  3. 力软:九年沉淀,从呱呱坠地到风华少
  4. matlab频域取样间隔,实验二 时域采样与频域采样及MATLAB程序
  5. 丘成桐大学生数学竞赛2014年分析与方程个人赛试题第一题另解
  6. 望夫山[阳江民俗文化]
  7. 综合训练(考勤系统)
  8. 一切发生的事,都是好事(19年总结)
  9. BNUZ程协技术部2020寒假任务简单版(后端)
  10. 剑指Offer题目:从扑克牌中随机抽 5 张牌,判断是不是顺子,即这 5 张牌是不是连续的。 2-10 为数字本身,A 为 1,J 为 11,Q 为 12,K 为 13,而大小王可以看成任意的 数字。