设计模式(7)—— 适配器模式

110 阅读5分钟

有些笔记本电脑的工作电压为20V,而我国标准的家庭用电是220V,二者无法适配。解决这一问题的方法就是加入一个适配器,经过适配器进行电压转换后,使二者可以兼容

软件开发中也会存在类似的不适配不兼容的情况,所以需要引入适配器,来解决这些不兼容问题

1. 问题引出

现在有一个已经编写好代码的算法库,算法库中实现了快速排序和二分查找算法

同时,还存在一个教务管理系统,里面的分数操作接口ScoreOperation中,也需要用到排序和查找等一系列功能,那么在这种情况下,通常的办法是对已经编写好的代码进行重用。

但现在存在两个问题:

  1. 算法库的代码是很久以前编写并部署好的,教务管理系统的开发者无法获取算法库中算法的源代码,也就是说,无法通过复制粘贴的方法,把算法库中的代码写到ScoreOperation接口中
  2. 由于两个系统是独立开发的,在开发过程中没有进行过协调,ScoreOperation接口中现有的代码无法直接调用算法库中的算法。例如:ScoreOperation中排序的接口名为sort,而算法库中快排的接口名为QuickSort,二者不匹配,无法直接调用。若要调用算法,只能去修改接口中的代码,而这又不是我们所期待的

所以,现在需要一个适配器对这两个不兼容的系统进行适配,使它们可以在同一个系统中协同工作

2. 适配器模式介绍

定义:将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以协同工作,适配器又名包装器

在适配器模式中引入了一个称为适配器的包装类,而被适配器包装的对象叫做适配者,即被适配的类。

适配器的实现实际上就是把客户端的请求转换成对适配者相应接口的调用

当客户端调用适配器的方法时,适配器在内部自动调用相应的适配者接口,内部过程对客户端而言是透明的

适配器模式包含3种角色:

  1. Target(目标抽象类)
  2. Adapter(适配器类)
  3. Adaptee(适配者类)

从结构图中可以看到,客户端调用Target类的request方法,而Adaptee类中没有该方法,只有实现相同功能的specificRequest方法,适配器Adapter类的作用就是将对request方法的调用转化成对specificRequest方法的调用

3. 用适配器模式重用算法库中的算法

目标类:ScoreOperation

public interface ScoreOperation {

    int[] sort(int[] array); //成绩排序

    int search(int[] array, int key); //成绩查找
}

适配者1:QuickSort

public class QuickSort {
    public int[] quickSort(int[] nums){
        int low = 0, high = nums.length - 1;
        sort(nums, low, high);
        return nums;
    }

    public void sort(int[] nums, int low, int high){
        if(low < high){
            int pivotPos = partition(nums, low, high);
            sort(nums, low, pivotPos - 1);
            sort(nums, pivotPos + 1, high);
        }
    }

    public int partition(int[] nums, int low, int high){
        int pivot = nums[low];
        while(low < high){
            while(low < high && nums[high] > pivot) high--;
            nums[low] = nums[high];
            while(low < high && nums[low] < pivot) low++;
            nums[high] = nums[low];
        }
        nums[low] = pivot;
        return low;
    }
}

适配者2:BinarySearch

public class BinarySearch {
    public int binarySearch(int[] nums, int key){
        int low = 0, high = nums.length - 1;
        while(low <= high){
            int mid = low + (high - low) / 2;
            if(nums[mid] == key){
                //找到元素返回下标
                return mid;
            }
            if(nums[mid] < key){
                low = mid + 1;
            }else{
                high = mid - 1;
            }
        }
        return -1; //未找到元素返回-1
    }
}

适配器类:OperationAdapter


public class OperationAdapter implements ScoreOperation{

    private QuickSort sortObj; //定义适配者QuickSort对象
    private BinarySearch searchObj; //定义适配者BinarySearch对象

    public OperationAdapter() {
        sortObj = new QuickSort();
        searchObj = new BinarySearch();
    }

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

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

客户端类:Client

public class Client {
    public static void main(String[] args) {
        ScoreOperation operation = new OperationAdapter();
        int[] scores = {84, 76, 50, 69, 90, 91, 88, 96};
        int[] result;
        int position;

        System.out.println("成绩排序结果");
        result = operation.sort(scores);

        //输出成绩
        System.out.println(Arrays.toString(result));

        //查找成绩90
        System.out.println("查找成绩90:");
        position = operation.search(result, 90);
        if(position == -1){
            System.out.println("不存在成绩90!");
        }else {
            System.out.println("成绩90对应下标为: " + position);
        }

        //查找成绩92
        System.out.println("查找成绩92:");
        position = operation.search(result, 92);
        if(position == -1){
            System.out.println("不存在成绩92!");
        }else {
            System.out.println("成绩92对应下标为: " + position);
        }
    }
}

程序运行结果如下

4. 双向适配器

若在适配器中,同时包含对目标类的引用和对适配者的引用,则这个适配器就是一个双向适配器

双向适配器中,适配者可以通过适配器调用目标类的方法,目标类也可以通过适配器调用适配者的方法

双向适配器的实现较复杂,一个典型的实现方式如下

public class Adapter {
    private Target target;
    private Adaptee adaptee;

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

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

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

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

5. 缺省适配器

当不需要实现接口中的所有方法时,可以先建立一个抽象类对目标接口进行实现,并且对每一个方法都给出一个默认实现(可以为空方法),那么该抽象类的子类就可以选择性地覆盖父类的某些方法来进行功能的实现

缺省适配器包含3种角色:

  1. ServiceInterface(适配者接口)
  2. AbstractServiceClass(缺省适配器类)
  3. ConcreteServiceClass(具体业务类)

缺省适配器模式,使得适配器模式变得更加灵活