设计模式:适配器模式(类适配器、对象适配器、接口适配器)

适配器模式的工作原理:

将一个类的接口转换为另一种接口,让原本接口不兼容的类可以兼容

从用户的角度是看不到适配器的,是解耦的;用户调用的是适配器转化后的目标接口方法,适配器再调用被适配者的相关接口方法。(比如用户只用type-C,要的是这个口给出的5v电压,而适配器去插插孔),这样对于用户来说,只是目标和接口交互。

1|0一、类适配器模式

类适配器会有一个 Adapter 类,通过继承 src(被适配者) 类,实现 dst(目标) 接口,完成从 src -> dst 的适配。

这几个之间的关系:

比如充电器是 Adapter , 220V 交流电是 src ,5V直流电是 dst。也就是说,目标是要插 dst ,但是只有 src 是源,src 需要被适配,src 是被适配者,适配器是 Adapter。

如上类图所示:

  1. Phone 和接口 Dest 关联,为了的得到 5V 电压,和接口关联更加适合扩展,Dest就是我们的 dst;
  2. Src220V就是我们的src,提供220V的交流电,需要被适配;
  3. Adapter就是我们说的Adapter,完成把 src 转换为 dst 的功能,他实现了 dst 接口,继承了 src 类。
/*
    被适配者
*/
public class Src220V {
    public int output220V(){
        int src = 220;
        System.out.println("电压 = "  + src + "V");
        return src;
    }
}
/*
    目标dst,5V电压,是接口
*/
public interface Dest5V {
     public int output5V();
}
/*
    类适配器Adapter,继承被适配类src,实现dst
*/
public class Adapter extends Src220V implements Dest5V{
    @Override
    public int output5V() {
        //获取到源电压
        int src = output220V();
        //转成目标电压
        int dst = src / 44;
        return dst;
    }
}
/*
    手机直接依赖的是适配器Adapter,但是是通过Dest5V接口依赖的
*/
public class Phone {
    //充电
    public void chongdian(Dest5V dest5V){
        //这里其实肯定是 5
        if (dest5V.output5V() == 5)
        System.out.println("电压为 5 V,可以正常充电");
    }
}

那么简单的测试就可以写一个客户端:

public class Client {
    public static void main(String[] args) {
        System.out.println("=== 类适配器模式 ===");
        Phone phone = new Phone();
        phone.chongdian(new Adapter());
    }
}
 

总结:

  1. 因为 java 是单继承机制,所以类适配器 Adapter 需要继承 src ,算是一个缺点(一直强调尽量不要继承)。因为这个要求 dst 必须是接口,不能让 Adapter 同时继承两个类,有一定局限性;
  2. src 的方法都会在 Adapter中暴露出来,增加了使用成本;
  3. 优点:因为 Adapter 继承了 src 类,所以可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强。

2|0二、对象适配器模式


 

对象适配器模式的基本思路和类适配器是相同的,只是将 Adapter 类稍作修改,不是继承 src 类,而是持有一个 src 的实例,解决兼容性的问题,即:持有 src 类,实现 dst 接口,完成 src -> dst 的适配。

根据设计模式的第七个原则:合成复用原则,尽量不要继承,而用别的替代,这就是对象适配器改进的思路。

和上一种 类适配器模式 一样,用手机充电的例子来修改代码,看一下类图的改变:

src 不再是被继承,而是以一个实例的方式出现在 Adapter 里,其他地方做相应的改变即可。

  1. 被适配者 :不变;
  2. 目标dst : 不变;
  3. 适配器 Adapter:改变
/*
    对象适配器Adapter,聚合一个被适配类src对象,实现dst
*/
public class Adapter implements Dest5V{
    //聚合一个对象
    private Src220V src220V;
    //提供构造器
    public Adapter(Src220V src220V) {
        this.src220V = src220V;
    }

    @Override
    public int output5V() {
        int dst = 0;
        if (src220V != null){
            //获取到源电压
            int src = src220V.output220V();
            System.out.println("=== 使用对象适配器 ===");
            dst = src / 44;
        }
        return dst;
    }
}
 
  1. 使用者Phone :不变;

Client:响应改变,调用方式变了,需要 src 对象。

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

总结:

和类适配器是一种思想,不过根据合成复用原则,将 Adapter 必须继承 src 的局限性问题优化,变成聚合一个对象。

3|0三、接口适配器模式


 

一些书也把接口适配器模式叫做 : 缺省适配器模式,或者适配器模式。

当不需要全部实现接口提供的方法的时候,可以先设计一个抽象类来实现接口(都实现成一个空方法),那么接着抽象类的子类就可以有选择的覆盖父类的方法来实现需求。适用于一个接口不想使用其所有的方法的情况。

对于上面的例子,可以不用直接实现 dst 这个接口,而是改成实现一个 AbstractAdapter,都实现成空方法,这样的话,继续让 Phone 依赖抽象类,就能用匿名内部类的方式重写某些方法。

改动比较小,因此就不做代码示例了。

4|0四、适配器模式应用


 

在 SpringMVC 框架里,源码的 HanlderAdapter 就使用了适配器模式。

猜你喜欢

转载自blog.csdn.net/Java0258/article/details/108013722