适配器模式
一、适配器模式
先说说什么是适配器。其实生活中有很多例子。比如2008年的时候买了一台Macbook,有一次做课程设计的时候在课堂上做演示汇报,需要连教室的投影仪。上去的时候突然发现,晕,Macbook的投影接口和教室投影仪的接口根本就对不上,顿时石化。。。后来赶紧去网上查了一下,发现Macbook的投影接口与其他品牌的PC是不一样的,需要另外花钱买一个转接器,还好有淘宝,最后花了差不多20大洋终于搞定。这里说的转接器其实就是一个所谓的适配器。它承担着让不同接口之间实现协同工作的功能。家里的水管和各种各样的水龙头之间很多时候不能直接接上,中间也许要一些其他的管子、零件什么的在中间。这些管子往往两头口子大小是不一样的。
上面的例子只是为了方便理解,可能并不那么准确,那下面我们说说适配器模式的定义:Convert the interface of a class into another interface clients expect。适配器模式能让原本互相接口不能匹配的类变得能够一起工作。也就是说,适配器将一个类的接口变成客户端所期望的另外一个接口。
适配器模式有两种类型:类的适配器模式和对象的适配器模式。
类的适配器模式比较通用的类图如图所示:
如图所示,适配器Adapter将被适配的对象的接口转换成客户端需要的Target所规定的接口。通过适配器,客户端可以将Adaptee类当作一个Target来与之协同工作。我们举个例子。我们首先定义目标Target接口,Target接口之规定了一个方法:request(),其代码如下所示:
package dp.adapter; public interface Target { public void request(); }
源对象Adaptee代码如下:
package dp.adapter; public class Adaptee { public void doSth() { System.out.println("做我自己的事情"); } }
对于适配器来说,要达到适配的作用,需要继承源对象Adaptee并且实现目标接口Target,我们看看代码:
package dp.adapter; public class Adapter extends Adaptee implements Target{ @Override public void request() { super.doSth(); } }
这样,客户端可以通过调用Target对象的request方法,享受到实际上Adaptee对象的doSth中提供的服务。这样一来,接口不匹配的问题就搞定了,代码如下:
package dp.adapter; public class Client { public static void main(String[] args) { Target t = new Adapter(); t.request(); } }
运行一下,结果如下:
对象的适配器模式如图所示:
在这种适配器模式中,适配器类Adapter和源Adaptee之间的关系不再是集成关系,而是一种委托的关系。Adapter往往持有一个Adaptee的引用。我们将上面的代码例子中Adapter的代码进行修改:
package dp.adapter; public class Adapter2 implements Target{ private Adaptee adaptee; @Override public void request() { adaptee.doSth(); } }
我们运行Client端程序,结果没有什么变化。
二、JDK中用到适配器模式的例子
在JDK中,适配器是一个非常重要的设计模式。尤其是在Java IO包的设计上,使用很广泛。在Java的IO包中有一对类,分别叫做InputStreamReader和OutputStreamWriter。他们负责对字节流和字符流进行转换,因此有的资料称他们为桥梁类。尽管名字叫桥梁类,但是他们并不是那个桥梁模式的应用。而是典型的适配器模式的应用。
对于InputStreamReader来说,它一个字节流到字符流的适配器,它的实现主要采用的是对象适配器模式,如图所示:
对于OutputStreamWriter来说也是类似的。
三、适配器模式的使用场景
适配器模式实际上是一个补偿型模式,一般不会在进行系统设计的时候用到。往往当你打算修改正在服役的系统的某个接口时,可以考虑是否用的上。
save me from myself