java设计模式之适配器模式

结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

以下情况使用适配器模式
• 你想使用一个已经存在的类,而它的接口不符合你的需求。
• 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
• (仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
 适配器的作用:直白来说,就是客户端定义接口,其中某个方法为实现某功能。但是现有的用于完成该功能的类Adaptee有自己的特殊实现方式,此时该类并没有和客户端的接口有继承关系。于是,此时需要出现一个类Adapter用于连接客户端接口和完成功能的类,Adapter实现客户端接口,然后在完成该功能的方法中把Adaptee类的特殊实现函数包含进去(主要有两种方式,一种是Adapter继承Adaptee用于继承该特殊实现函数,另一种是在Adapter中定义一个Adaptee成员变量,用以调用Adaptee的特殊实现函数)。

其实现方式主要有两种:
类适配器

对象适配器

著名的设计模式“四人帮”这样评价适配器模式:

将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。——Gang of Four

适配器模式将一个类的接口适配成用户所期待的。一个适配器通常允许因为接口不兼容而不能一起工作的类能够在一起工作,做法是将类自己的接口包裹在一个已存在的类中,即用一个类来实现该接口,重写该接口中的方法,已达到将原来接口中不符合客户需求的方法的实现方式修改为符合要求的实现方式。

Adapter 设计模式主要目的组合两个不相干类,常用有两种方法,第一种解决方案是修改各自类的接口。但是如果没有源码,或者不愿意为了一个应用而修改各自的接口,则需要使用 Adapter 适配器,在两种接口之间创建一个混合接口。

图 1. 适配器模式类图
图 1. 适配器模式类图

图 1 所示是适配器模式的类图。Adapter 适配器设计模式中有 3 个重要角色:被适配者 Adaptee,适配器 Adapter 和目标对象 Target。其中两个现存的想要组合到一起的类分别是被适配者 Adaptee 和目标对象 Target 角色,按照类图所示,我们需要创建一个适配器 Adapter 将其组合在一起。

其中:
• Target
— 定义Client使用的与特定领域相关的接口。
• Client
— 与符合Ta rg e t接口的对象协同。
• Adaptee
— 定义一个已经存在的接口,这个接口需要适配。
• Adapter 
— 对Adaptee的接口与Target接口进行适配

具体的实现代码如下:

类适配器

package com.visionsky.DesignPattern; 
 
interface Target { 
    void Request(); 
} 
 
class Adaptee { 
    void SpecificRequst() { 
        System.out.println("Adaptee's SpecificRequst"); 
    } 
} 
 
class Adapter extends Adaptee implements Target  
{ 
 
    @Override 
    public void Request() { 
         System.out.println("Adapter's Request"); 
          super.SpecificRequst(); 
    } 
 
} 
 
public class AdapterDemo { 
 
    /** 
     * @param args 
     */ 
    public static void main(String[] args) { 
        // TODO Auto-generated method stub 
        Target t=new Adapter(); 
        t.Request(); 
    } 
 
} 
对象适配器

package com.visionsky.DesignPattern; 
 
interface Target { 
    void Request(); 
} 
 
class Adaptee { 
    void SpecificRequst() { 
        System.out.println("Adaptee's SpecificRequst"); 
    } 
} 
 
class Adapter  implements Target  
{ 
 
    private Adaptee adaptee; 
    public Adapter() 
    { 
        this.adaptee=new Adaptee(); 
    } 
     
    @Override 
    public void Request() { 
         System.out.println("Adapter's Request"); 
         adaptee.SpecificRequst(); 
    } 
 
} 
 
public class AdapterDemo { 
 
    /** 
     * @param args 
     */ 
    public static void main(String[] args) { 
        // TODO Auto-generated method stub 
        Target t=new Adapter(); 
        t.Request(); 
    } 
 
} 
生活中的例子:
在生活中最简单的例子就是电源适配器,如手机适配器,我们的家用电源是220V的,但是,对于手机来说,根本大不了这么多,所以,在这种情况下,需要电源适配器来为我们提供适合手机的电压。

package com.visionsky.DesignPattern; 
 
public class AdaterDemoInLife { 
 
    /** 
     * @param args 
     */ 
    public static void main(String[] args) { 
        // TODO Auto-generated method stub 
        MobilePowerAdapter mpa=new MobilePowerAdapter(); 
        mpa.GetPower10V(); 
    } 
 
} 
 
interface ITarget { 
    int GetPower10V(); 
} 
 
class Power { 
    int GetPower220V() { 
         return 220; 
    } 
} 
 
class MobilePowerAdapter  implements ITarget  
{ 
    private Power power; 
    public MobilePowerAdapter( ) { 
          
        this.power = new Power(); 
    } 
     
    @Override 
    public int GetPower10V() { 
        // TODO Auto-generated method stub 
         power.GetPower220V(); 
         //将220v转变成10v,具体做法就不写了, 大家明白就行 
         return 10; 
    } 
} 

适配器模式在开源项目中的应用

JDK 中有大量使用适配器模式的案例,清单 11 大致列举了一些类。

使用适配器模式的类
java.util.Arrays#asList()
javax.swing.JTable(TableModel)
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()
JDK1.1 之前提供的容器有 Arrays,Vector,Stack,Hashtable,Properties,BitSet,其中定义了一种访问群集内各元素的标准方式,称为 Enumeration(列举器)接口。

Enumeration 接口实现方式
Vector v=new Vector();
for (Enumeration enum =v.elements(); enum.hasMoreElements();) {
Object o = enum.nextElement();
processObject(o);
}
JDK1.2 版本中引入了 Iterator 接口,新版本的集合对象(HashSet,HashMap,WeakHeahMap,ArrayList,TreeSet,TreeMap, LinkedList)是通过 Iterator 接口访问集合元素的。

Iterator 接口实现方式
List list=new ArrayList();
for(Iterator it=list.iterator();it.hasNext();)
{
 System.out.println(it.next());
}
这样,如果将老版本的程序运行在新的 Java 编译器上就会出错。因为 List 接口中已经没有 elements(),而只有 iterator() 了。那么如何将老版本的程序运行在新的 Java 编译器上呢? 如果不加修改,是肯定不行的,但是修改要遵循“开-闭”原则。我们可以用 Java 设计模式中的适配器模式解决这个问题。
采用适配器模式
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

public class NewEnumeration implements Enumeration
{

 Iterator it;
 public NewEnumeration(Iterator it)
 {
 this.it=it;
 // TODO Auto-generated constructor stub
 }

 public boolean hasMoreElements()
 {
 // TODO Auto-generated method stub
 return it.hasNext();
 }

 public Object nextElement()
 {
 // TODO Auto-generated method stub
 return it.next();
 }
 public static void main(String[] args)
 {
 List list=new ArrayList();
 list.add("a");
 list.add("b");
 list.add("C");
 for(Enumeration e=new NewEnumeration(list.iterator());e.hasMoreElements();)
 {
 System.out.println(e.nextElement());
 }
 }
}

Java I/O 库大量使用了适配器模式,例如 ByteArrayInputStream 是一个适配器类,它继承了 InputStream 的接口,并且封装了一个 byte 数组。换言之,它将一个 byte 数组的接口适配成 InputStream 流处理器的接口。

我们知道 Java 语言支持四种类型:Java 接口,Java 类,Java 数组,原始类型(即 int,float 等)。前三种是引用类型,类和数组的实例是对象,原始类型的值不是对象。也即,Java 语言的数组是像所有的其他对象一样的对象,而不管数组中所存储的元素类型是什么。这样一来的话,ByteArrayInputStream 就符合适配器模式的描述,是一个对象形式的适配器类。FileInputStream 是一个适配器类。在 FileInputStream 继承了 InputStrem 类型,同时持有一个对 FileDiscriptor 的引用。这是将一个 FileDiscriptor 对象适配成 InputStrem 类型的对象形式的适配器模式。

同样地,在 OutputStream 类型中,所有的原始流处理器都是适配器类。ByteArrayOutputStream 继承了 OutputStream 类型,同时持有一个对 byte 数组的引用。它一个 byte 数组的接口适配成 OutputString 类型的接口,因此也是一个对象形式的适配器模式的应用。

FileOutputStream 继承了 OutputStream 类型,同时持有一个对 FileDiscriptor 对象的引用。这是一个将 FileDiscriptor 接口适配成 OutputStream 接口形式的对象型适配器模式。

Reader 类型的原始流处理器都是适配器模式的应用。StringReader 是一个适配器类,StringReader 类继承了 Reader 类型,持有一个对 String 对象的引用。它将 String 的接口适配成 Reader 类型的接口。

Spring 中使用适配器模式的典型应用

在 Spring 的 AOP 里通过使用的 Advice(通知)来增强被代理类的功能。Spring 实现这一 AOP 功能的原理就使用代理模式(1、JDK 动态代理。2、CGLib 字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类,并在代理类的方法前,设置拦截器,通过执行拦截器中的内容增强了代理方法的功能,实现的面向切面编程。

Advice(通知)的类型有:BeforeAdvice、AfterReturningAdvice、ThrowSadvice 等。每个类型 Advice(通知)都有对应的拦截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。Spring 需要将每个 Advice(通知)都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对 Advice 进行转换。

MethodBeforeAdvice 类
public interface MethodBeforeAdvice extends BeforeAdvice { 
 
 void before(Method method, Object[] args, Object target) throws Throwable; 
 
} 
public interface MethodBeforeAdvice extends BeforeAdvice {

void before(Method method, Object[] args, Object target) throws Throwable;

}
Adapter 类接口
public interface AdvisorAdapter { 
 
 boolean supportsAdvice(Advice advice); 
 
 MethodInterceptor getInterceptor(Advisor advisor); 
 
} 
public interface AdvisorAdapter {

boolean supportsAdvice(Advice advice);

MethodInterceptor getInterceptor(Advisor advisor);

}
MethodBeforeAdviceAdapter 类
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { 
 
 public boolean supportsAdvice(Advice advice) { 
 return (advice instanceof MethodBeforeAdvice); 
 } 
 
 public MethodInterceptor getInterceptor(Advisor advisor) { 
 MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice(); 
 return new MethodBeforeAdviceInterceptor(advice); 
 } 
 
} 
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}

public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}

}
DefaultAdvisorAdapterRegistry 类
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable { 
 
 private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3); 
 
 
 /** 
 * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters. 
 */ 
 public DefaultAdvisorAdapterRegistry() {//这里注册了适配器 
 registerAdvisorAdapter(new MethodBeforeAdviceAdapter()); 
 registerAdvisorAdapter(new AfterReturningAdviceAdapter()); 
 registerAdvisorAdapter(new ThrowsAdviceAdapter()); 
 } 
 
 
 public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException { 
 if (adviceObject instanceof Advisor) { 
 return (Advisor) adviceObject; 
 } 
 if (!(adviceObject instanceof Advice)) { 
 throw new UnknownAdviceTypeException(adviceObject); 
 } 
 Advice advice = (Advice) adviceObject; 
 if (advice instanceof MethodInterceptor) { 
 // So well-known it doesn't even need an adapter. 
 return new DefaultPointcutAdvisor(advice); 
 } 
 for (AdvisorAdapter adapter : this.adapters) { 
 // Check that it is supported. 
 if (adapter.supportsAdvice(advice)) {//这里调用了适配器的方法 
 return new DefaultPointcutAdvisor(advice); 
 } 
 } 
 throw new UnknownAdviceTypeException(advice); 
 } 
 
 public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { 
 List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3); 
 Advice advice = advisor.getAdvice(); 
 if (advice instanceof MethodInterceptor) { 
 interceptors.add((MethodInterceptor) advice); 
 } 
 for (AdvisorAdapter adapter : this.adapters) { 
 if (adapter.supportsAdvice(advice)) {//这里调用了适配器的方法 
 interceptors.add(adapter.getInterceptor(advisor)); 
 } 
 } 
 if (interceptors.isEmpty()) { 
 throw new UnknownAdviceTypeException(advisor.getAdvice()); 
 } 
 return interceptors.toArray(new MethodInterceptor[interceptors.size()]); 
 } 
 
 public void registerAdvisorAdapter(AdvisorAdapter adapter) { 
 this.adapters.add(adapter); 
 } 
 
}

类适配器和对象适配器的选择

  1. 从实现上:类适配器使用对象继承的方式,属于静态的定义方式。对象适配器使用对象组合的方式,属于动态组合的方式;

  2. 从工作模式上:类适配器直接继承了 Adaptee,使得适配器不能和 Adaptee 的子类一起工作。对象适配器允许一个 Adapter 和多个 Adaptee,包括 Adaptee 和它所有的子类一起工作;

  3. 从定义角度:类适配器可以重定义 Adaptee 的部分行为,相当于子类覆盖父类的部分实现方法。对象适配器要重定义 Adaptee 很困难;

  4. 从开发角度:类适配器仅仅引入了一个对象,并不需要额外的引用来间接得到 Adaptee。对象适配器需要额外的引用来间接得到 Adaptee。

总的来说,建议使用对象适配器方式。













评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值