1. 适配器模式定义
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作.(注:该处的接口是指广义的接口,可以表示一个方法或者方法的集合)
2. 结构图
3. 角色分析
- Target(目标抽象类):定义客户所需接口,可以是一个抽象类,接口或具体类
- Adapter(适配器类):可调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,是适配器模式的核心,在对象适配器模式中,通过继承一个Target并关联一个Adaptee对象使二者产生联系
- Adaptee(适配者类):即被适配的角色
4. 核心代码
Class Adapter extends Target{
private Adaptee adaptee; //维持一个对适配者对象的引用
public Adapter(Adaptee adaptee){
this.adaptee=adaptee;
}
public void request(){
adaptee.specificRequest(); 转发调用
}
}
5. 实践案例
1) 方案结构图
2) 实现代码
package com.zach.adapter;
//抽象成绩操作类:目标接口
public interface ScoreOperation {
public int[] sort(int array[]); //成绩排序
public int search(int array[],int key); //成绩查找
}
package com.zach.adapter;
//快速排序类:适配者
public class QuickSort {
public int[] quickSort(int array[]){
sort(array,0,array.length-1);
return array;
}
public void sort(int array[], int p, int r) {
// TODO Auto-generated method stub
int q=0;
if(p<r){
q=partition(array,p,r);
sort(array,p,q-1);
sort(array,q+1,r);
}
}
public int partition(int[] a, int p, int r) {
int x=a[r];
int j=p-1;
for(int i=p;i<=r-1;i++){
if(a[i]<=x){
j++;
swap(a,j,i);
}
}
swap(a,j+1,r);
return j+1;
// TODO Auto-generated method stub
}
public void swap(int[] a, int i, int j) {
// TODO Auto-generated method stub
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
package com.zach.adapter;
//二分法查找类:适配者
public class BinarySearch {
public int binarySearch(int array[],int key){
int low = 0;
int high = array.length-1;
while(low<=high){
int mid = (low +high)/2;
int midVal = array[mid];
if(midVal<key){
low = mid+1;
}else if(midVal >key){
high = mid-1;
}else {
return 1; //找到元素
}
}
return -1; //未找到元素返回-1
}
}
package com.zach.adapter;
//操作适配器:适配器
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);
}
@Override
public int search(int[] array, int key) {
// TODO Auto-generated method stub
return searchObj.binarySearch(array, key); //调用适配者类BinarySearch的查找方法
}
}
package com.zach.adapter;
//客户端代码
public class Client {
public static void main(String[] args) {
//可以将类配置在配置文件中,提高扩展性,此处就不展示
ScoreOperation operation = new OperationAdapter();
int scores[] = {84,76,50,69,90,91,88,96};
System.out.println("成绩排序结果:");
int result[] = operation.sort(scores);
//遍历输出成绩
for (int i : result) {
System.out.print(i+",");
}
System.out.println();
int score = 0;
System.out.println("查找成绩90:");
score = operation.search(result, 90);
if(score !=-1){
System.out.println("找到成绩90.");
}else{
System.out.println("没有找到成绩90.");
}
System.out.println("查找成绩92:");
score = operation.search(result, 92);
if(score !=-1){
System.out.println("找到成绩92.");
}else{
System.out.println("没有找到成绩92.");
}
}
}
6. 类适配器模式
类适配器模式与对象适配器模式最大的区别在于其适配器和适配者之间关系是继承关系
1) 类适配器模式结构图
2) 核心代码
class Adapter extends Adaptee implements Target{
public void request(){
specificRequest();
}
}
7. 双向适配器模式
在对象适配器模式使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类的方法,目标类也可以通过它调用适配者类中的方法,则该适配器就是一个双向适配器
1) 双向适配器结构图
2) 核心代码
class Adapter implements Target,Adaptee {
//同时维持对抽象目标类和适配者的引用
private Target target;
private Adaptee adaptee;
public Adatpter(Target target){
this.target = target;
}
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request(){
adaptee.specificRequest();
}
public void specificRequest(){
target.request();
}
}
8. 缺省适配器模式
缺省适配器模式是适配器模式的一种变体,即当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求;适用于不想使用一个接口中所有方法的情况,又称为单接口适配器模式
1) 缺省适配器模式结构图
2) 缺省适配器模式的角色分析
- ServiceInterface(适配器接口)一个声明了大量方法的接口
- AbstractServiceClass(缺省适配器类):使用空方法的形式实现了在ServiceInterface接口中声明的方法,通常将它定义为抽象类
- ConcreteServiceClass(具体业务类):缺省适配器的子类,在没有引入适配器之前,实现适配者接口,根据需要有选择性的覆盖在适配器类中定义的方法
9. 适配器模式总结
1) 优点
- 将目标类和适配者类解耦
- 将具体的业务实现过程封装在适配者类中,对于客户端是透明的,同一个适配者类可以在多个不同的系统中复用
- 可通过配置文件,方便的更换或新增适配器
2) 缺点
- 对于Java,C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者
- 适配者类不能为最终类
- 在Java,C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类
3) 使用场景
- 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码
- 想创建一些可以重复使用的类,用于一些彼此没有太大关联的类,包括一些可能在将来引进的类一起工作