0.参考资料



1.概述

  • 将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 —《设计模式》GoF
    • 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作.
  • 核心:
  • 分类
  • 工作原理
    • 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
    • 从用户的角度看不到被适配者,是解耦的
    • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
    • 用户收到反馈结果,感觉只是和目标接口交互,如图
      • 7}]{5VK9P{SSWJ6SL67_I)E.png

1.1动机

  1. - 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
  2. - 如何应对这种“迁移的变化”? 如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?

1.2结构

  1. - ![0GOS__L]76@(8UK(YWX@BLX.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628836312457-8edd8306-1f83-447c-91ce-13523935d78d.png#clientId=u7f2d4b32-688b-4&from=paste&height=208&id=ucddea8e7&margin=%5Bobject%20Object%5D&name=0GOS__L%5D76%40%288UK%28YWX%40BLX.png&originHeight=415&originWidth=1081&originalType=binary&ratio=1&size=83386&status=done&style=none&taskId=u530d4e06-a0fd-46b5-9c13-79c6022153b&width=540.5)

2.要点总结

宏观架构

  1. 1. Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、 类库迁移等方面非常有用。
  2. 1. GoF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用"多继承"的实现方式,一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
  3. 1. Adapter模式可以实现的非常灵活,不必拘泥于GoF23中定义的两种结构。例如完全可以将Adapter模式中的 “现存对象”作为新的接口方法参数来达到适配的目的。

微观代码

  1. 1. 三种命名方式,是根据 src(被适配类)是以怎样的形式给到Adapter(即在Adapter里的形式)来命名的。
  2. - 类适配器:以类形式,在Adapter里,就是将src当做类,**继承**
  3. - 对象适配器:以对象形式,在Adapter里,将src作为一个对象,**组合**
  4. - 接口适配器:以接口形式,在Adapter里,将src作为一个接口,**实现**
  5. 2. Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
  6. 2. 实际开发中,实现起来不拘泥于我们讲解的三种经典形式

3.案例

需求

  1. - 以生活中充电器的例子来类比适配器,充电器本身相当于Adapter220V交流电相当于src (即被适配者),我们的dst(即 目标)是5V直流电
  2. - ![9N)O`4{Y8OD%YCF)@($VJKA.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628840649102-fffe9934-1130-4d8e-9013-9d6fa3656b93.png#clientId=u7f2d4b32-688b-4&from=paste&height=122&id=u0079a15f&margin=%5Bobject%20Object%5D&name=9N%29O%604%7BY8OD%25YCF%29%40%28%24VJKA.png&originHeight=244&originWidth=1464&originalType=binary&ratio=1&size=175233&status=done&style=none&taskId=uf7497397-e227-49b6-949c-70fe4b8f502&width=732)

4.使用模式


4.1类适配器模式

概述

  1. - Adapter类,通过继承 src类(被适配类),实现 dst 类接口(目标类),完成src->dst的适配。

方案

类图

  1. - ![%((ZZA2`(EVL0S)PYQK2Q_6.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628842146301-78e8dfe7-8603-4830-abcf-b1bcaaa23542.png#clientId=u7f2d4b32-688b-4&from=paste&height=223&id=uc9e2ec83&margin=%5Bobject%20Object%5D&name=%25%28%28ZZA2%60%28EVL0S%29PYQK2Q_6.png&originHeight=445&originWidth=1252&originalType=binary&ratio=1&size=208195&status=done&style=none&taskId=ua6aa5036-6277-4dcf-8b69-e2f9a6d8f56&width=626)

代码

  1. - 基础类: V220, V5
  1. /**
  2. * @description: 被适配类
  3. */
  4. public class V220 {
  5. public int output220V(){
  6. int src = 220;
  7. System.out.println("输出电压" + src + "V");
  8. return src;
  9. }
  10. }
  11. /**
  12. * @description: 目标类
  13. */
  14. public interface V5 {
  15. /**
  16. * @description:
  17. * @return: int 返回电压(实际理论为5V)
  18. */
  19. int output5V();
  20. }
  1. - (类)适配器类: ClassAdapter
  1. /**
  2. * @description: 类适配器. 实现目标接口, 继承被适配类
  3. */
  4. public class MyClassAdapter extends V220 implements V5 {
  5. @Override
  6. public int output5V() {
  7. int desc;
  8. int src = output220V();
  9. desc = src / 44; // 适配处理. 可能很复杂
  10. System.out.println("输出电压" + desc + "V");
  11. return desc;
  12. }
  13. }
  1. - 客户端及其它: Client, Phone
  1. public class Client {
  2. public static void main(String[] args) {
  3. Phone phone = new Phone();
  4. phone.input(new MyClassAdapter());
  5. }
  6. }
  7. public class Phone {
  8. // 充电
  9. public void input(MyClassAdapter MyClassAdapter){
  10. int v = MyClassAdapter.output5V();
  11. if (v == 5){
  12. System.out.println(" 电压5V, 正常, 开始充电...");
  13. } else {
  14. System.out.println(" 电压" + v + "v, 不正常! 不充电!");
  15. }
  16. }
  17. }
  1. - 测试结果
  1. 输出电压220V
  2. 输出电压5V
  3. 电压5V, 正常, 开始充电...

要点总结

  1. 1. Java是单继承机制,所以类适配器需要继承src类(被适配器类)这一点算是一个缺点, 因为这要求dst(目标类)必须是接口,有一定局限性;
  2. 1. src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
  3. 1. 由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵 活性增强了。

4.2对象适配器模式

概述

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

方案

类图

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628843617124-5c60ffc8-3d75-474f-a883-3d58302296b5.png#clientId=u7f2d4b32-688b-4&from=paste&height=244&id=u66288cc2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=487&originWidth=1197&originalType=binary&ratio=1&size=290270&status=done&style=none&taskId=u8d2a4eed-793a-4c01-8ac6-402d41a6f5d&width=598.5)

代码

  1. - (对象)适配器类
  1. /**
  2. * @description: 对象适配器. 实现目标接口, 组合被适配类
  3. */
  4. public class MyObjectAdapter implements V5 {
  5. // 使用组合解耦
  6. V220 v220;
  7. MyObjectAdapter(V220 v220){
  8. this.v220 = v220;
  9. }
  10. @Override
  11. public int output5V() {
  12. int desc;
  13. int src = v220.output220V();
  14. desc = src / 44; // 适配处理. 可能很复杂
  15. System.out.println("输出电压" + desc + "V");
  16. return desc;
  17. }
  18. }
  1. - 客户端及其它: Client, Phone
  1. public class Client {
  2. public static void main(String[] args) {
  3. Phone phone = new Phone();
  4. phone.input(new MyObjectAdapter(new V220()));
  5. }
  6. }
  7. public class Phone {
  8. // 充电
  9. public void input(MyObjectAdapter myObjectAdapter){
  10. int v = myObjectAdapter.output5V();
  11. if (v == 5){
  12. System.out.println(" 电压5V, 正常, 开始充电...");
  13. } else {
  14. System.out.println(" 电压" + v + "v, 不正常! 不充电!");
  15. }
  16. }
  17. }
  1. - 测试结果
  1. 输出电压220V
  2. 输出电压5V
  3. 电压5V, 正常, 开始充电...

要点总结

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

4.1接口(缺省)适配器模式

概述

  1. - 又名:适配器模式(Default Adapter Pattern)或缺省适配器模式。
  2. - 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
  3. - 适用于一个接口不想使用其所有的方法的情况

实例

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628843518920-9b52c564-eaf3-46b3-b5ec-7dc5f29d1f44.png#clientId=u7f2d4b32-688b-4&from=paste&height=278&id=u4fc9f779&margin=%5Bobject%20Object%5D&name=image.png&originHeight=851&originWidth=1460&originalType=binary&ratio=1&size=604951&status=done&style=none&taskId=u4a2b3777-14bc-4be9-aeb6-7eb3c2360d8&width=477)
  2. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628843551433-79a806f2-4e44-43f5-943a-87340f676c70.png#clientId=u7f2d4b32-688b-4&from=paste&height=204&id=u625383d9&margin=%5Bobject%20Object%5D&name=image.png&originHeight=407&originWidth=1007&originalType=binary&ratio=1&size=72068&status=done&style=none&taskId=ue71c99e8-a3f3-40c9-9e8c-0032f0d5c8f&width=503.5)

5.经典使用


5.1SpringMVC中的HandlerAdapter

原因分析

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

源码分析

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628846436286-437aadbb-942a-4ea9-9c4a-8d59fc5ca55d.png#clientId=u7f2d4b32-688b-4&from=paste&height=309&id=u375251fa&margin=%5Bobject%20Object%5D&name=image.png&originHeight=618&originWidth=1452&originalType=binary&ratio=1&size=368474&status=done&style=none&taskId=u7d14f78c-0bb4-42a7-85b6-ced37de9c82&width=726)

说明

  1. - Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类
  2. - 适配器代替controller执行相应的方法
  3. - 扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了

6.模拟SpringMVC中的DispatcherServlet

  1. - 根据[适配器模式在SpringMVC中应用](#puB6G) 进行模拟SpringMVC DispatcherServlet

代码

  1. - 类图
  2. - ![PV2_FA2O15_A]5A)[T5775M.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629185412732-cc507f40-183e-44fe-abab-7530650adfc3.png#clientId=uda108c4a-4489-4&from=paste&height=257&id=u0044d8cf&margin=%5Bobject%20Object%5D&name=PV2_FA2O15_A%5D5A%29%5BT5775M.png&originHeight=514&originWidth=898&originalType=binary&ratio=1&size=111522&status=done&style=none&taskId=ue1e3aca4-e573-42d7-b09a-b1da68a0e61&width=449)
  3. - Handler 被适配类
  1. /**
  2. * @description: (抽象的)被适配类
  3. */
  4. public interface Handler {
  5. }
  6. /**
  7. * @description: 具体的被适配类
  8. */
  9. class HttpHandler implements Handler{
  10. public void doHttpHandler(){
  11. System.out.println("http...");
  12. }
  13. }
  14. class SimpleHandler implements Handler{
  15. public void doSimpleHandler(){
  16. System.out.println("simple...");
  17. }
  18. }
  19. class AnnotationHandler implements Handler{
  20. public void doAnnotationHandler(){
  21. System.out.println("annotation...");
  22. }
  23. }
  1. - HandlerAdapter 适配器类
  1. /**
  2. * @description: 抽象的适配器类
  3. */
  4. public interface HandlerAdapter {
  5. /**
  6. * @description: 传入的Handler是否是该(具体)适配器的被适配类
  7. * @param: handler 传入的handler
  8. * @return: boolean 是否匹配
  9. */
  10. boolean isSupports(Handler handler);
  11. /**
  12. * @description: 传入一个被适配类, (通过适配器)执行其方法
  13. * @param: handler 被适配类
  14. * @return: void
  15. * @date: 2021/8/13 19:31
  16. */
  17. void handler(Handler handler);
  18. }
  19. /**
  20. * @description: 具体的适配器类
  21. */
  22. class HttpHandlerAdapter implements HandlerAdapter {
  23. @Override
  24. public boolean isSupports(Handler handler) {
  25. return handler instanceof HttpHandler;
  26. }
  27. @Override
  28. public void handler(Handler handler) {
  29. ((HttpHandler) handler).doHttpHandler();
  30. }
  31. }
  32. class SimpleHandlerAdapter implements HandlerAdapter{
  33. @Override
  34. public boolean isSupports(Handler handler) {
  35. return handler instanceof SimpleHandler;
  36. }
  37. @Override
  38. public void handler(Handler handler) {
  39. ((SimpleHandler) handler).doSimpleHandler();
  40. }
  41. }
  42. class AnnotationHandlerAdapter implements HandlerAdapter{
  43. @Override
  44. public boolean isSupports(Handler handler) {
  45. return handler instanceof AnnotationHandler;
  46. }
  47. @Override
  48. public void handler(Handler handler) {
  49. ((AnnotationHandler) handler).doAnnotationHandler();
  50. }
  51. }
  1. - DispatcherServlet 控制器. 及测试main
  1. public class DispatcherServlet {
  2. // 模拟职责链
  3. public static List<HandlerAdapter> handlerAdapters = new ArrayList<>();
  4. protected DispatcherServlet(){
  5. handlerAdapters.add(new HttpHandlerAdapter());
  6. handlerAdapters.add(new SimpleHandlerAdapter());
  7. handlerAdapters.add(new AnnotationHandlerAdapter());
  8. }
  9. /**
  10. * @description: 为每一个handler返回对应的适配器
  11. * @param: h 目标handler
  12. * @return: HandlerAdapter 对应的适配器
  13. */
  14. public HandlerAdapter getHandlerAdapter(Handler h){
  15. for (HandlerAdapter ha : handlerAdapters) {
  16. // 遍历适配器, 找到每个处理器对应的适配器. 并最终返回
  17. if (ha.isSupports(h)) {
  18. return ha;
  19. }
  20. }
  21. return null;
  22. }
  23. /**
  24. * @description: 模拟Spring启动后, 从request请求或其它途径拥有的多个Handler
  25. * @param:
  26. * @return: com.ryze.mySpringMVC.Handler[]
  27. * @date: 2021/8/13 19:51
  28. */
  29. protected Handler[] initHandlers(){
  30. Handler[] handlers = new Handler[10];
  31. Handler[] srcHandler = new Handler[]{new HttpHandler(), new SimpleHandler(), new AnnotationHandler()};
  32. for (int i = 0; i < handlers.length; i++){
  33. int randomIndex = (int) (Math.random() * 3);
  34. handlers[i] = srcHandler[randomIndex];
  35. }
  36. return handlers;
  37. }
  38. /**
  39. * @description: 执行所有的处理器Handler
  40. */
  41. public void doHandler(){
  42. // 获取所有的处理器
  43. Handler[] handlers = initHandlers();
  44. int count = 0;
  45. // 对每一个处理器进行处理
  46. for (Handler h : handlers) {
  47. System.out.print((++count) + " : " + h.getClass().getSimpleName() + "\t\t");
  48. // 调用本类方法, 获取每个handler对应的适配器
  49. HandlerAdapter ha = getHandlerAdapter(h);
  50. ha.handler(h);
  51. }
  52. }
  53. /**
  54. * @description: 启动类
  55. */
  56. public static void main(String[] args) {
  57. new DispatcherServlet().doHandler();
  58. }
  59. }
  1. - 测试结果
  1. 1 : SimpleHandler simple...
  2. 2 : HttpHandler http...
  3. 3 : HttpHandler http...
  4. 4 : SimpleHandler simple...
  5. 5 : AnnotationHandler annotation...
  6. 6 : AnnotationHandler annotation...
  7. 7 : AnnotationHandler annotation...
  8. 8 : HttpHandler http...
  9. 9 : AnnotationHandler annotation...
  10. 10 : SimpleHandler simple...