Java设计模式之适配器模式

步入正题之前可先预习一下之前有讲过的

设计模式七大原则

单例模式

工厂模式

原型模式

建造者模式


无场景,不设计。我们来看看以下需求,

假设你国外度假,去的是德国,工匠精神的大国。出国之前你要准备随身必备用品,衣服,手机电脑,充电器……然而在充电器上德国使用的是欧标(也就是两孔插座)你需要自己准备一个转换器,才可以使用其插座进行充电。这个转换器也就是咱们所要讲的适配器


Java设计模式之适配器模式

好了接下来步入正题

适配器模式(Adapter Pattern)

生活中的例子

比如大家熟知的游戏机有港版。国行等,

港版的插头是三孔插头

国行的插头是两孔插头

此时你使用两孔插座用港版的三孔插头的话就需要一个转换器适配器

Java设计模式之适配器模式


概念及其原理

适配器(Adapter Pattern)将某个类的接口转换成客户端期望的另外一个接口时;主要目的是兼容性,让原本因接口不匹配不能在一起工作的两个类可以协同工作,其别名包装器

主要分为三类:

  1. 类适配器模式

  2. 对象适配器模式

  3. 接口适配器模式

原理:

  • 适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容

  • 从用户角度看不到适配者,是解耦的。

  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配的相关接口和方法

  • 用户收到反馈结果,感觉只是和目标接口交互


原理图

Java设计模式之适配器模式

刚刚有提到适配器模式一共分为三种,祥光的理论知识也就这些;那么接下来我们通过实际案例俩进行讲解剖析(注意语义环境根据上图所示后面不再单独解释):

dst(destination):目标最终需要的输出即 Target

Adappter:适配器类

src(source):被适配者,需要适配的类、接口、对象、简称src

类适配器模式

案例:

比如上述所说的生活中充电器的例子:

充电器本身充当Adapter(适配器类),220v交流电压相当于src(被适配者)我们的手机需要的是5v直流电相当于dst(即Target)

UML类图

Java设计模式之适配器模式

代码演示

Voltage220V(被适配的类)

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

适配接口

//适配接口
public interface IVoltage5V {
 public int output5V();
}

适配器类(Adapter)

//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

 @Override
 public int output5V() {
  // TODO Auto-generated method stub
  //获取到220V电压
  int srcV = output220V();
  int dstV = srcV / 44 ; //转成 5v
  return dstV;
 }

}

Phone去使用(依赖IVoltage5V,但是传入的是VoltageAdapter)

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, 不能充电~~");
  }
 }
}

测试使用

public class Client {

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

}

注意事项和细节

Java是单继承机制,所以类适配器需要继承被适配者,这算是一个缺点,因为这要求目标必须是接口,有一定的局限性。

被适配者的方法在Adapter中暴露出来,增加了使用成本

由于继承了被适配者,所以它可以根据需求重写目标类的方法,使得Adapter的灵活性增强了

以上就是类适配器的基本介绍


对象适配器模式

基本思路和类的适配器模式相同,只是将Adapter类做改,不是继承被适配类,而是持有目标类的实例,以解决兼容性问题。即持有被适配类,实现目标类接口,完成被适配类目标类的适配

根据“合成复用原则”,在系统中尽量使用关联关系(聚合)来代替继承关系

常用的一种

利用对象适配器模式针对以上案例进行改进:

核心思想就是主要修改适配器类

UML类图

Java设计模式之适配器模式

主要修改的代码(VoltageAdapter类、Client类)其他的同上

  • VoltageAdapter类

//适配器类
public class VoltageAdapter  implements IVoltage5V {

 private Voltage220V voltage220V; // 关联关系-聚合
 
 
 //通过构造器,传入一个 Voltage220V 实例
 public VoltageAdapter(Voltage220V voltage220v) {
  
  this.voltage220V = voltage220v;
 }



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

}
  • Client类

public class Client {

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  System.out.println(" === 对象适配器模式 ====");
  Phone phone = new Phone();
  phone.charging(new VoltageAdapter(new Voltage220V())); // 主要变化是这里
 }

}

注意事项

对象适配器模式和类适配器模式算是同一种思想,只不过实现方式不同。

根据合成复用原则,使用聚合替代继承,所以他解决了类适配器必须继承被适配类的局限性,也不再要求目标类是接口

以上就是对象适配器模式


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

核心思想:

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并未该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类中的某些方法来实现需求

图解

Java设计模式之适配器模式

代码实现

先做一个接口

public interface Interface4 {
 public void m1();
 public void m2();
 public void m3();
 public void m4();
}

抽象类

//在AbsAdapter 我们将 Interface4 的方法进行默认实现
public abstract class AbsAdapter implements Interface4 {

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

 }

 public void m2() {

 }

 public void m3() {

 }

 public void m4() {

 }
}

Client使用

public class Client {
 public static void main(String[] args) {
  
  AbsAdapter absAdapter = new AbsAdapter() {
   //只需要去覆盖我们 需要使用 接口方法
   @Override
   public void m1() {
    // TODO Auto-generated method stub
    System.out.println("使用了m1的方法");
   }
  };
  
  absAdapter.m1();
 }
}


以上就是适配器设计模式的全部内容,讲了这么多,不免有小伙伴问道,那实际的应用场景在哪里呢?听我慢慢给你道来。

适配器模式在Spring MVC框架中应用的源码解析

Spring MVC中的HandlerAdapter,就是使用了适配器模式


当一个请求来的时候 DispatcherServlet 会用 doDispatch() 这个方法进行处理 doDispatch() 会接受 request 里面有一个核心方法getHandler() 会根据processRequest(request) 得到一个 Handler(控制器);然后利用getHandlerAdapter返回一个适配器。(这里不同的Handler返回不同的适配器)。核心源码截图

Java设计模式之适配器模式


Java设计模式之适配器模式


关于getHandlerAdapter这个方法返回的是一个HandlerAdapter(是一个接口)


Java设计模式之适配器模式

Java设计模式之适配器模式

HandlerAdapter具体实现的类


Java设计模式之适配器模式

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



概括:


大体就是一面是Controller(也就是Handler),其下面有各种的实现类Controller;另一面是Adapter,它下面也有具体的Adapter。在doDipatch里面依赖了Controller和Adapter,先根据你的请求拿到Controller,再根据Controller(Handler)返回一个Adapter,然后让Adapter调用Controller方法进行一个处理。通过Adapter适配器关联起来对Controller的调用


以上就是SpringMVC源码中应用适配器模式的大致流程,不知道小伙伴看的有些懵懵的。那么我们来手写一个MVC,通过Adapter设计模式来获取对应的Controller源码。

代码解析:

  • Controller

//多种Controller实现  
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...");
 }
}
  • HandlerAdapter

HandlerAdapter的实现类具体是怎么使用到Controller的具体实现类的呢?

会调用supports()方法判断你传进来的Handler是不是该类型,如果是会调用handle()方法进行一个类型的强转,然后调用这个类型Controller里面的doSimplerHandler()方法

///定义一个Adapter接口 
public interface HandlerAdapter {
 public boolean supports(Object handler);

 public void handle(Object handler);
}

// 多种适配器类

class SimpleHandlerAdapter implements HandlerAdapter {

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

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

}

class HttpHandlerAdapter implements HandlerAdapter {

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

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

}

class AnnotationHandlerAdapter implements HandlerAdapter {

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

 public boolean supports(Object handler) {

  return (handler instanceof AnnotationController);
 }

}
  • DispatchServlet

通过组合的方式将HandlerAdapter(多个)放进去

会依赖Controller

public class DispatchServlet {

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

 public DispatchServlet() {
  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) {
  for (HandlerAdapter adapter : this.handlerAdapters) {
   if (adapter.supports(controller)) {
    return adapter;
   }
  }
  return null;
 }

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

}


主要流程:

doDispatch() 里面先根据请求拿到Controller,再根据Controller(也可以叫handler)获取对应的HandlerAdapter,然后通过HandlerAdapter去调用Controller里面的方法。这样就通过适配器的方式建立了Controller和HandlerAdapter这两者之间的联系

图解:

Java设计模式之适配器模式

说明:

Spring通过定义一个适配接口,使得每一种Controller有对应的适配器实现类

适配器代替controller执行相应的方法

扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

这就是设计模式的力量





小结

三种命名方式:是根据被适配类是以怎样的形式给到Adapter(在Adapter中的形式)来命名的

  1. 类适配器:以类给到,在Adapter里,就是将被适配的当作类,继承

  2. 对象适配器:以对象给到,在Adapter里,将被适配的作为一个对象,持有(组合、聚合)

  3. 接口适配器:以接口给到,在Adapter里,将被适配的作为一个接口,实现(接口)

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

实际开发中并不拘泥于这三种方式

好了,本次的适配器模式到此就讲解完了。

欢迎点赞,关注!


原文始发于微信公众号(码上遇见你):Java设计模式之适配器模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/79017.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!