结构模式之适配器模式
视频地址:https://www.bilibili.com/video/BV1bt4y1U7YA/
适配器模式(Adapter pattern)
1.定义
"Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces."
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作
又称包装器(Wrapper),既可以作为类结构型模式,也可以作为对象结构型模式。
使用前提或场景:解决两个已有接口间不兼容问题。Client面向接口编程,而该面向的接口又与第三方接口不兼容,两者又不便于修改,则可用Adapter协调工作
个人理解:不能说是"转换",而是协调不兼容接口间工作。比如客户端期望调用的方法传参是一个样子(面向接口编程,该方法可上升为目标抽象类,下述角色中有提到),而现有第三方实现的满足业务功能的接口可用,但规定的参数格式不一样。但两者(目标抽象类与第三方接口)都不愿或者不能修改,因此,则需要一个适配器来协调两者工作。
2.模式结构
示例代码:
public class Adapter implements DataOperation { private BinarySearch search; private QuickSort sort; public Adapter(){ search = new BinarySearch(); sort = new QuickSort(); } @Override public void sort(int[] array) { sort.quickSort(0,array.length-1,array); } @Override public int search(int[] array, int key) { return search.BinarySearch(0,array.length-1,key,array); } }
public class BinarySearch { public int BinarySearch(int low, int high, int key, int[] array){ if(low>high) return -1; int mid = (low+high)/2; if(array[mid] == key) return mid; int temp = BinarySearch(low,mid-1,key,array); if(temp!=-1) return temp; temp = BinarySearch(mid+1,high,key,array); if(temp!=-1) return temp; return -1; } }
import java.util.Arrays; /** * 客户端面向接口DataOperation编程,具体实例通过IOC或其他随便什么方式注入。 * 但又有现成的接口可以调用,即:BinarySearch和QuickSort * 但两边代码都不想或者不能修改,于是写个Adapter,协调两个接口工作 */ public class Client { public static void main(String[] args){ DataOperation operation; operation = new Adapter(); //此处直接new,只是为了方便. int[] ary = {4,1,1,2,1,9,3,2,1,1}; operation.sort(ary); System.out.println(Arrays.toString(ary)); } }
public interface DataOperation { public void sort(int[] array); public int search(int[] array, int key); }
public class QuickSort { public void quickSort(int low, int high, int[] array){ if(low >= high) //i=low+1 是因为不考虑第一个分界元素 return; int i=low+1,j=high; //i=low+1 是因为不考虑第一个分界元素 while(i<=j){ //带上"="是因为,当只有两个元素时(即当low+1=high时),会导致不进循环,直接交换 while(i<=high&&array[i]<=array[low]) //带'='是因为判断最高边界为最后一个元素,i 最后可能会越界,但必然不会发生交换 i++; while(j>low&&array[j]>array[low]) //不带'=',是因为需判断最低边界只有第2个元素。j 可以移到第一个分区元素去,但必然不会发生交换 j--; if(i<j) //即正常情况下 switchIndex(i,j,array); //数组中交换元素位置的方法 } //结束时,i有3种情况: // 1.稳定交换后(ij即将发生交叉后下次),i必定会移到第一个大元素。 // 2.后面全都比它小,导致i越界1位 // 3.i也是指向第一个大元素(指向第一个,是因为就算先前有大元素,也肯定已经交换了),但是是因为j发生越界而终止。 //总之无论哪种,--i都会是最后一个小元素,即分隔元素需要放置的位置 switchIndex(--i,low,array); quickSort(low,i-1,array); //对分区元素左右两边子数组递归调用此方法 quickSort(i+1,high,array); } private void switchIndex(int index1, int index2, int[] array){ int temp = array[index1]; array[index1] = array[index2]; array[index2] = temp; } }
包含如下角色:
-
Target(目标抽象类)
客户期望的业务接口,可以是具体类,也可以是接口。 -
Adapter(适配器类)
适配器类可调用Adaptee接口,以对 Adaptee 和 Target 进行适配,使其协调工作。 -
Adaptee(适配者类)
被适配的角色,定义了可工作、已存在、待适配的接口,些情况下甚至没有源代码。
4.Client(客户类)
客户类面对目标抽象类进行编程
可细分为两种模式:
类适配器模式:适配器类与适配者类是继承关系,因为Java不支持多重继承,因此该模式下目标抽象类只能是接口。
对象适配器:适配器类与适配者类是关联关系(也可以称为委派关系),即含有适配者类的成员变量
3.模式扩展
- 缺省适配器模式(Default Adapter Pattern):当不需要实现接口提供的全部方法时,可先设计一个抽象类(缺省适配器)来实现该接口,并为每个方法提供一个默认实现(通常是空实现,也称钩子方法[Hook Method]),那么该抽象类的子类(具体业务类)可有选择的只覆盖父类中某些方法来实现需求。
interface ServiceInterface{ void M1(); void M2(); void M3(); } abstract class AbstractServiceClass implements ServiceInterface{ public void M1(){} public void M2(){} public void M3(){} } class ConcreteServiceClass extends AbstractServiceClass{ public void M2(){ System.out.println("具体业务方法"); } }
2.双向适配器:适配器中同时包含对目标类和适配者类的引用。
4 优缺点
也是其使用场景,可以在不修改客户、适配者、目标抽象类前提下,让几者兼容工作。同时适配器也能很方便的替换,符合开闭原则。