适配器模式入门和应用

165 阅读5分钟

1.适配器功能

将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而导致无法在一起工作的两个类能够一起工作。

也就是说,当前系统存在两个接口A和B,客户端只支持访问A接口,但是当前系统没有A接口对象,有B接口对象,客户无法识别B接口,因此需要通过一个适配器C,将B接口内容转换成A接口,从而使得客户能够从A接口获取得到B接口的内容。

2.设配器模式涉及的角色

适配器模式一般包含三种角色:

  1. 目标角色(Target):也就是我们期望的接口。
  2. 源角色(Adaptee):存在于系统中,内容满足客户需求(需转化),但接口不匹配的接口实例。
  3. 适配器(Adapter):将源角色(Adaptee)转化为目标角色(Target)的类实例。

3.适配器模式的三种形式

适配器模式有3种形式:类适配器、对象适配器、接口适配器。

以充电器转换电压为例,我们的家用电是220V,手机充电是5V(假设,现在手机都高于5V)。我们已有220V的类,需要适配器转换为5V。

类适配器

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

IVoltage5V接口,表示适配接口,抽象输出5v的方法

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

VoltageAdapter类,表示适配器类,继承被适配类,又实现适配接口,实现220v->5v转换

 public class VoltageAdapter extends Voltage220V implements IVoltage5V{
     @Override
     public int output5V() {
         int srcV = output220V();//获取220v
         int dstV = srcV / 44;//转成5v
         return dstV;
     }
 }

Phone类,实现充电方法

 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,无法充电");
         }
     }
 }

Client类,客户端

 public class Client {
     public static void main(String[] args) {
         Phone phone = new Phone();
         phone.charging(new VoltageAdapter());
     }
 }

对象适配器

Voltage220V类,表示被适配类,输出220v电压

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

IVoltage5V接口,表示适配接口,抽象输出5v的方法

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

VoltageAdapter类,表示适配器类,实现适配接口,通过聚合关系持有Voltage220V类而不再是继承,实现220v->5v转换

 public class VoltageAdapter implements IVoltage5V {
     private Voltage220V voltage220V;
  
     //根据构造器,传入一个Voltage220V,聚合关系
     public VoltageAdapter(Voltage220V voltage220V) {
         this.voltage220V = voltage220V;
     }
  
     @Override
     public int output5V() {
         int dstV = 0;
         if (null != voltage220V) {
             int srcV = voltage220V.output220V();//获取220v
             dstV = srcV / 44;//转成5v
         }
         return dstV;
     }
 }

Phone类,实现充电方法

 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,无法充电");
         }
     }
 }

Client类,客户端

 public class Client {
     public static void main(String[] args) {
         System.out.println("对象适配器模式");
         Phone phone = new Phone();
         phone.charging(new VoltageAdapter(new Voltage220V()));
     }
 }

相比于类适配器的继承,聚合实现了解耦,解决了继承的局限性,更加灵活,聚合优于继承。

接口适配器

类适配器和对象适配器着重于将系统存在的一个角色(Adaptee)转化成目标接口(Target),由A转成B的过程。

而接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,类显得臃肿。

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

例如:接口Interface4有四个接口方法,我目标实现不需要实现这么多方法。

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

AbsAdapter抽象类,实现接口,并使接口每个方法默认空实现

 public abstract class AbsAdapter implements Interface4{
     //默认实现
     @Override
     public void m1() {
  
     }
  
     @Override
     public void m2() {
  
     }
  
     @Override
     public void m3() {
  
     }
  
     @Override
     public void m4() {
  
     }
 }

我的目标类就可以只实现我需要的抽象方法,比如我仅仅需要实现m1方法:

 public class Target extends AbsAdapter{
     @Override
     public void m1() {
         System.out.println("只想使用m1的方法,只需覆盖m1的方法");
     }
 ​
 }

在java8之后,我们定义接口方法时可以有默认实现,这样我们就不需要再增加一个抽象类做默认实现了,所以接口适配器在java8之后意义不大。

例如:在SpringMVC框架中的WebMvcConfigurer,有很多默认实现的方法,按需实现即可。

 public interface WebMvcConfigurer {
     default void configurePathMatch(PathMatchConfigurer configurer) {
     }
 ​
     default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
     }
 ​
     default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
     }
 ​
     default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
     }
 ​
     default void addFormatters(FormatterRegistry registry) {
     }
 }

4.适配器的应用

4.1 在Spring Cloud GateWay中的应用

GatewayFilterAdapter是一个对象适配器:目的将GlobalFilter适配GatewayFilter。

SpringCloud GateWay中有两种过滤器,一种是GatewayFilter,一种是GlobalFilter。现在需要将GlobalFilter转换成GatewayFilter。GatewayFilter就是我们的目标角色,GlobalFilter就是源角色(Adaptee)。

 public class GatewayFilterAdapter implements GatewayFilter {
 ​
     private final GlobalFilter delegate;
 ​
     GatewayFilterAdapter(GlobalFilter delegate) {
         this.delegate = delegate;
     }
 ​
     @Override
     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
         return this.delegate.filter(exchange, chain);
     }
 }

适配器中包含一个GlobalFilter对象,适配器实现了GatewayFilter接口,当GatewayFilter时,适配器会将具体的任务委托给GlobalFilter去做。这样就完成了从GlobalFilter到GatewayFilter的转变。

这个例子也可以说是委托模式,把GatewayFilter委托给GlobalFilter去处理任务,但是从功能用途角度也可以说是适配器模式,因为把GlobalFilter适配为GatewayFilter了。