设计模式-适配器模式

71 阅读5分钟

概念

将某个类的接口转换成客户端期望的另一个接口的表示,主要目的是兼容性,让原本因接口不匹配而不能一起工作的两个类可以协同工作

以手机充电的场景,就是家用插线板不能直接用电线链接手机进行充电,需要一个充电器,而充电器的作用就是转换电压,让原本不匹配的手机和电源可以协同工作

以实际业务为例,在Java 代码中 service 层就是一个适配器,service层持有dao的对象,从dao层查询数据,在service层处理转换为前端需要的数据,并返回至前端。

应用场景

  1. 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
  2. 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

具体应用

场景:手机电源适配器,将家用电源220V电压转换为手机所能接受的5V电压

基本代码,在后面的不同适配器方法中会用到

首先需要一个220V电压的类

public class Voltage220V {

    public int output220(){
        System.out.println("输出电压220V");
        return 220;
    }
}

准备一个输出电压为5V的接口

public interface Voltage5V {
    public int output5V();
}

准备一个可以充电的手机

public class Phone {

    private String name;

    public Phone(String name){
        this.name = name;
    }
    
    public void charging(Voltage5V v) {
        if(v.output5V() == 5){
            System.out.println("手机"+name+"电压5V 开始充电```````````");
        }else{
            System.out.println("电压不为5v 不能充电");
        }
    }
}

下面看一下

类适配器

UML图

image-20211024131444431

具体代码

从图中可以知道,需要适配器继承被适配类,同时由于他需要提供输出5V的功能,所以他还需要实现Voltage5V的接口。

参考代码:

public class VoltageAdapter extends Voltage220V implements Voltage5V {


    @Override
    public int output5V() {
        System.out.println("电压转换中---------");
        int inputV = output220();
        int outputV = inputV / 44;
        return outputV;
    }
}

客户端调用:

public class Client {

    public static void main(String[] args) {
        System.out.println("--------------------类适配器--------------------");
        Phone phone = new Phone("1号手机");
        phone.charging(new VoltageAdapter());
    }
}

//运行结果

image-20211024132427826

对象适配器

UML图

image-20211024132607201

具体代码

从图中可以看到,原本的被适配者不是被继承,而是被适配器持有用来解决兼容性问题。这里满足合成复用原则,在系统中尽量使用关联关系来替代继承关系。对象适配器模式是最常用的一种

适配器代码:

public class VoltageAdapter implements Voltage5V {
    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V){
        this.voltage220V = voltage220V;
    }

    @Override
    public int output5V() {
        System.out.println("电压转换中---------");
        int inputV = voltage220V.output220();
        int outputV = inputV / 44;
        return outputV;
    }
}

客户端调用:

public class Client {

    public static void main(String[] args) {
        System.out.println("--------------------对象适配器--------------------");
        Phone phone1 = new Phone("2号");
        phone1.charging(new VoltageAdapter(new 	Voltage220V()));
    }
}

运行结果:

image-20211024133223025

接口适配器(缺省适配器)

接口适配器适用于一种情况,就是某个接口对外提供多个方法,但是,使用者只想使用其中某个方法,不愿意完全实现这个接口的所有方法,这个时候可以使用接口适配器

其原理是,可以先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以有选择的覆盖父类提供的某些方法来实现需求

UML 图

image-20211024133555876

具体代码

适配者接口:


/**
 * 服务接口有多个
 */
public interface ServiceInterface {

    void m1();
    void m2();
    void m3();
    void m4();
}

缺省适配器类:

这个类是一个抽象类,先将接口的所有方法进行空实现

public abstract class AbstractServiceClass implements ServiceInterface  {

    @Override
    public void m1() {

    }

    @Override
    public void m2() {

    }

    @Override
    public void m3() {

    }

    @Override
    public void m4() {

    }
}

客户端:

在调用时,重写想要调用的逻辑

public class Client {

    public static void main(String[] args) {

        //在具体业务中,只想使用接口的m1方法,
        AbstractServiceClass service = new AbstractServiceClass(){
            @Override
            public void m1() {
                System.out.println("重写m1逻辑调用");
            }
        };

        service.m1();
    }
}

运行结果:

image-20211024133944779

适配器模式优缺点

优点

  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构
  • 增加了类的透明性和复用性,将具体的业务逻辑封装在了适配者类中对于客户端类而言是透明的
  • 类适配器模式,因为是继承的关系,所以子类可以重写适配者类的方法,提高了适配器的灵活性
  • 对象适配器模式,一个对象适配器可以把多个不同的适配者适配到同一个目标中

缺点

  • 类适配器模式,Java单继承机制,类适配器需要继承 被适配类,算是一个缺点,因为要求输出的必须是一个接口,有一定的局限性
  • 对象适配器模式,与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。