《Java Design Patterns》第七章 适配器模式 - 不兼容结构的协调

第七章 适配器模式 - 不兼容结构的协调

一、概念

如笔记本的电源接口只支持20V电压,而家庭用电的电压为220V,肯定无法直接通过一根电线直接为笔记本提供电源。此时,就需要一个变压器电源,来将220V电压转换为20V电压。这个变压器就是适配器模式的一种。

适配器模式:将一个类的接口与另一个类的接口匹配利用,而无须修改原来的适配者,解耦合抽象目标类接口。

适配器模式可以是类结构型模式,也可以是对象结构型模式。

1.2 适配器模式中的几个角色

  1. Target - 目标抽象类
    1. 目标抽象类定义客户所需接口
    2. 抽象类、接口或具体类
    3. 类似上面的笔记本
  2. Adapter - 适配器类
    1. 适配器可以调用另一个接口,作为一个转换器,对 Adaptee 和 Target 进行适配
  3. Adaptee - 适配者类
    1. 被适配的角色(如220V的家庭电源,我们需要将其转换为20V电压输出)
    2. 它定义了一个已经存在的接口,这个接口需要适配
    3. 适配者类一般是个具体类,包含了客户希望使用的业务方法

1.3 对象适配器 - 实例

// 抽象成绩操作类 - Target目标接口
interface ScoreOperation {
    public int[] sort(int array[]); // 成绩排序
    public int search(int array[],int key); // 成绩查找
}

// 快速排序类 - Adaptee 适配者类
class QuickSort{
    public int[] quickSort(int array[]){
        // 自己实现
    }
}

// 二分查找类 - Adaptee 适配者类
class BinarySearch{
    public int binarySearch(int array[],intkey){
        // 自己实现
    }
}

// 操作适配器 - Adapter 适配器
class OperationAdapter implements ScoreOperation{
    private QuickSort sort = new QuickSort;
    private BinarySearch search = new BinarySearch();

    // 调用适配者类QuickSort的排序方法
    public int[] sort(int array[]){
        return sort.quickSort(array);
    }

    // 调用适配者类BinarySearch的查找方法
    public int search(int array[],int key){
        return search.binarySearch(array,key);
    }
}

1.4 类适配器

类适配器与对象适配器的区别在于:

对象适配器与适配者之间的关系是关联关系,而类适配器中的关系则是继承关系

如下:

class Adapter extends Adaptee implements Target{
    public void request(){
        specificRequest();
    }
}

由于Java、C#等语言不支持多重继承,因此类适配器在使用时受到诸多限制。如Target必须是接口,Adapter不能是Final类等等。

1.5 双向适配器

在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者和目标类可以通过它调用彼此的方法,那么该适配器就是一个双向适配器。如下:

class Adapter implements Adaptee,Target{
    private Target target;
    private Adaptee adaptee;

    public Adapter(Target target){
        this.target = target;
    }

    public Adapter(Adaptee adpatee){
        this.adaptee = adaptee;
    }

    public void request(){
        adaptee.specificRequest();
    }

    public void specificRequest(){
        target.request();
    }
}

1.6 缺省适配器

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

二、总结

优点

  1. 目标类与适配者类解耦,通过引入一个适配器类来重用现有的适配者类
  2. 增加类的透明性和复用性,具体业务过程封装在适配者类中,对客户端透明,提供了适配者的复用性,同一个适配者类可以在多个不同系统中复用
  3. 灵活性和扩展性好,可以通过配置文件的方式更换适配器,符合“开闭原则”

缺点

  1. Java、C#不支持多重继承,一次只能适配一个适配者类
  2. 适配者类不能为最终类
  3. Java、C#中,类适配器模式的目标抽象类必须是接口

适用场景

  1. 系统需要使用一些现有的类,但该类的接口不符合系统需要
  2. 创建一个可重复使用的类,如上面的快速排序,可以做成一个适配者,为其它适配器类所调用

三、练习

// SD卡接口及其具体实现
public interface SDCard {
    String readSD(); // 读SD卡
    int writeSD(String message); // 写SD卡
}
class SDCardImpl implements SDCard {
    public String readSD() {
        return "读取SD卡";
    }
    public int writeSD(String message) {
        // 假装写入数据至SD卡中了
        return 0;
    }
}

// 电脑类,具有读取SD卡的功能
class Computer {
    String readSD(SDCard sdCard) {
        return sdCard.readSD();
    }
}

// ----- 新版的TF卡上市,系统需要在不修改原有SD卡接口的情况下,支持新版的TF卡  -----

// TF卡接口及其实现
interface TFCard {
    String readTF(); // 读TF卡
    int writeTF(String message); // 写TF卡
}
class TFCardImpl implements TFCard {
    public String readTF() {
        return "读取TF卡";
    }
    public int writeTF(String message) {
        return 0;
    }
}

// TF卡的适配器,继承与目标接口,但是将SD卡接口适配为TF卡的接口
class TFCardAdapter implements SDCard {
    private TFCard tfCard;
    public TFCardAdapter(TFCard tfCard) {
        this.tfCard = tfCard;
    }
    @Override
    public String readSD() {
        return tfCard.readTF();
    }
    @Override
    public int writeSD(String message) {
        return tfCard.writeTF(message);
    }
}

// 测试客户端
class Client {
    public static void main(String[] args) {
        SDCard sdCard = new SDCardImpl();
        SDCard tfCard = new TFCardAdapter(new TFCardImpl()); // 通过适配器,将TFCard适配为SDCard

        Computer computer = new Computer();
        String s = computer.readSD(sdCard);
        String s1 = computer.readSD(tfCard);
        System.out.println(s);
        System.out.println(s1);
    }
}

适配器类的名称尽量做到见名知意,因为客户端表面上调用的是SDCard,但是内部却被适配成了TFCard,如果系统中存在大量这种代码,会使系统十分的混乱。

如果适配器大量存在的话,可以考虑重构代码,而不是继续适配下去。

文章作者: koral
文章链接: http://luokaiii.github.io/2019/07/06/读书笔记/《JavaDesignPatterns》/9.适配器模式/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自