适配器模式是23种设计模式中比较常用的模式之一,在创建型模式、结构性模式和行为型模式分类中,适配器模式归类为结构型模式。
适配器一般可以分为对象适配器和类适配器,对象适配器和类适配器使用了两种不同的适配方法,它们分别使用的是对象的组合和继承。由于Java中并不支持多继承,所以Java中适配器模式更多的使用的是对象适配器模式,同样这也符合开发时的设计原则,多用组合少用继承。
public interface Target { void request(); } public class Adaptee { public void specificRequest(){ //TODO } } public class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { adaptee.specificRequest(); } } public class Client { public static void main(String[] args) { Adaptee adaptee=new Adaptee(); Target adapter=new Adapter(adaptee); adapter.request(); } }
在类适配器模式中目标类和被适配者类类似,只是适配器这里使用了继承机制继承了被适配者类。
public class Adapter extends Adaptee implements Target { @Override public void request() { specificRequest(); } } public class Client { public static void main(String[] args) { Target adapter = new Adapter(); adapter.request(); } }
缺省适配器模式为接口提供缺省实现,有时也被称为默认适配器模式或者单接口适配器模式。由于使用接口就必须实现接口中所有的方法,而在某些场景中或者在多数场景中我们仅仅是需要实现某几个方法即可,这时候缺省适配器则为我们提供了选择。缺省适配器在平常开发中多数开发者都应该或多或少使用过,有些开发者可能比较喜欢使用Abs-开始命名一些抽象类来实现接口,在一些GUI开发中可能比较常使用-Adapter作为后缀来实现某些接口。
缺省的适配器模式可以仅仅是对一个接口的缺省实现,也可以实现多个接口,如Android中AnimatorListenerAdapter,它的实现了Animator.AnimatorListener和Animator.AnimatorPauseListener接口,java.awt.event.WindowAdapter实现了WindowListener、WindowStateListener和WindowFocusListener接口。
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener, Animator.AnimatorPauseListener { ... } public abstract class WindowAdapter implements WindowListener, WindowStateListener, WindowFocusListener { ... }
缺省适配器模式一般情况下是一个抽象类,因为这个类不应该被实例化,它的实例也没有用处。缺省适配器可以对接口的所有方法都提供缺省实现,也可以只针对部分方法提供默认实现,对某些强制重写的方法仍然提供抽象方法或者直接不重写接口中方法。但是也有例外,有些缺省适配器本身就不是一个抽象类,所以在使用缺省适配器时需要根据自身逻辑进行设计。
public class ViewPropertyAnimatorListenerAdapter implements ViewPropertyAnimatorListener { ... }
在对象适配器的使用过程中,如果在适配器中同时包含对目标类和被适配者类的引用,被适配者可以通过它调用目标类中的方法,目标类也可以通过它调用被适配者类中的方法,那么该适配器就是一个双向适配器。
Android中使用了许多适配器模式,最常用如BaseAdapter或者PagerAdapter;
IO流中使用了对象适配器,如InputStreamReader和OutputStreamWriter;
public class InputStreamReader extends Reader { private final StreamDecoder sd; public String getEncoding() { return sd.getEncoding(); } public int read(char cbuf[], int offset, int length) throws IOException { return sd.read(cbuf, offset, length); } public boolean ready() throws IOException { return sd.ready(); } public void close() throws IOException { sd.close(); } ... }
在Spring AOP框架中,对BeforeAdvice、AfterAdvice、ThrowsAdvice三种通知类型借助适配器模式来实现。
public interface AdvisorAdapter{ //将一个Advisor适配成MethodInterceptor MethodInterceptor getInterceptor(Advisor advisor); //判断此适配器是否支持特定的Advice boolean supportsAdvice(Advice advice); }
GUI中设计的一些事件监听或者动画监听,如WindowAdapter和AnimatorListenerAdapter等。
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。 增加了类的透明性和复用性 ,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
在类适配器模式中,由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器中,一个对象适配器可以把多个不同的被适配者适配到同一个目标,也就是说,同一个适配器可以把被适配者类和它的子类都适配到目标接口。
类适配器模式的缺点是适配器类在很多编程语言中不能同时适配多个适配者类,对象适配器模式的缺点是很难置换适配者类的方法。过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
桥接模式的结构与对象适配器类似,但是适配器模式主要是为了解决两个已有接口之间不匹配的问题。它不考虑这些接口是怎样实现的,也不考虑它们各自可能会如何演化。这种方式不需要对两个独立设计的类中的任一个进行重新设计,就能够使它们协同工作。另一方面,桥接模式则对抽象接口与它的(可能是多个)实现部分进行桥接。虽然这一模式允许你修改实现它的类,它仍然为用户提供了一个稳定的接口。
桥接模式和适配器模式用于设计的不同阶段。桥接模式用于系统的初步设计,而在初步设计完成之后,当发现系统与已有类无法协同工作时,可以采用适配器模式,当然了这也不是绝对的。
装饰模式和适配器模式都有一个别名叫包装模式,但包装的形式是不一样的。装饰模式更倾向将一个接口转换为用户所需要的另外一个接口,也就是说它会将一个接口转换为另一个接口,但是适配器模式则是不会改变现有的接口,而是会加入责任。
外观模式定义一个新的接口,而适配器模式则复用一个原有的接口。适配器使两个已有的接口协同工作,而不是定义一个全新的接口。
《Head First 设计模式》
《Java设计模式》
《Java与设计模式》
《GOF设计模式》