适配器模式

来源:互联网 发布:ncbi下载基因组数据 编辑:程序博客网 时间:2024/06/11 19:39

适配器模式


      在《HeadFirst设计模式》一书中是这么来解释适配器模式的:将一个类的接口,转换成客户期望的另一个接口。适配器让原来的接口不兼容的类可以合作无间。

      现在有一个场景:现在已经有一个软件系统,为了使得这个软件系统更加强大,需要和另外一个厂商类库进行配合使用,由于两者又不兼容,那么该怎么办呢?



我们可以采取下面的方式,在这个软件系统和厂商类库之间创建一个适配器,让两者可以协同工作,在此,我们先不管这个适配器是如何创建的!


      先来一个不太恰当的例子但是容易理解,假设一个boss不知道鸡是什么,他只知道鸡可以飞,可以咯咯的叫,别的什么不知道。现在他对一个养殖老板说要100只鸡,但是呢养殖老板只有50只鸡,所以老板决定搞一些鸭子来糊弄一下这个不能区分鸡鸭的boss.好啦,先来定义两个接口:鸡、鸭。还要两个实现类,野鸡,野鸭!

鸡、鸭接口:

interface Chook {/** * 鸡能咯咯叫 */public  void  gege();/** * 鸡能飞 */public  void fly();}
interface Duck {/** * 鸭是嘎嘎叫 */public  void  gaga();/** * 鸭能飞 */public  void fly();}
两个实现类,野鸡,野鸭:

public class YeChook  implements  Chook{@Overridepublic void gege() {System.out.println("咯咯叫");}@Overridepublic void fly() {System.out.println("鸡飞");}}

public class YeDuck  implements  Duck{@Overridepublic void gaga() {System.out.println("嘎嘎叫");}@Overridepublic void fly() {System.out.println("鸭子飞");}}

要想用鸭来冒充鸡,还要一个适配器
public class ChookDuckAdapter implements  Chook{private Duck  duck;public ChookDuckAdapter(Duck duck) {this.duck = duck;}@Overridepublic void gege() {duck.gaga();}@Overridepublic void fly() {// TODO 自动生成的方法存根}}


测试类:

public class Test {public static void main(String[] args) {//先来50只鸡for (int i = 0; i < 5; i++) {Chook  chook=new YeChook();chook.gege();chook.fly();}//先来50只用鸭子来冒充的假鸡for (int i = 0; i < 5; i++) {Duck  duck=new YeDuck();//把鸭子包装成了鸡Chook chookDuck=new ChookDuckAdapter(duck);//调用这个假鸡的方法,实际上还是掉用的鸭chookDuck.gege();chookDuck.fly();}}}


这个模式可以通过创建适配器进行接口转换,让不兼容放入接口变成兼容,这可以让客户从实现的接口解耦。我们看到过这个模式运行时候的行为,现在来看看它的类图

适配器的工作流程如下:
首先,客户通过目标接口调用适配器的方法对适配器发出请求;
然后,适配器使用被适配者接口把请求转换成被适配者的一个或者多个调用接口;
最后,客户收到调用的结果,但并未察觉这一切是适配器在器转换作用。

我们再看一个例子:

早期的集合类型(例如:Vector,Stack,Hashtable)都实现了一个名为elements的方法,该方法会返回一个枚举(Enumeration),但是随着JDK的升级,更新集合类时,开始使用了Iterator的接口,这个接口和枚举接口很像,都可以让你遍历集合类型内的每个元素,但是不同的是,迭代器还是提供了删除元素的方法;而今天经常面对遗留代码,这些遗留代码暴露出枚举接口,但是我们又希望在新的代码中只是用迭代器。想解决这个问题,看来我们需要构造一个适配器。

interface  Enumeration<E> {/** * @return  枚举中是否有下一个元素 */public  boolean hasMoreElements();/** * @return  返回此枚举的下一个元素 */public  E nextElement();}

interface Iterator<T> {/** * @return   集合中是否有下一个元素 */public  boolean  hasNext();/** * @return  返回集合中的元素 */public  T  next();/** * 移除集合中最后一个元素 */public  void remove();}


转换适配器:
public class EnumerationIterator<T> implements Iterator<T> {private Enumeration<T> mEnumeration;public EnumerationIterator(Enumeration<T> mEnumeration) {this.mEnumeration = mEnumeration;}@Overridepublic boolean hasNext() {return mEnumeration.hasMoreElements();}@Overridepublic T next() {return mEnumeration.nextElement();}@Overridepublic void remove() {System.out.println("枚举不能移除,我们也可以抛出一个异常!");}}


     其实适配器主要有两种,一种是对象适配器,一种是类适配器类适配器需要多重继承才能实现它,这在JAVA中是不可能的。对象适配器利用组合方式将请求传送给适配者。所以对象适配器和类适配器使用两种不同的适配方法(分别是组合与继承),这两种实现究竟有什么差异呢?


  在上图中可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,提供一个中间环节,即类Adapter,把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是继承关系,这决定了这个适配器模式是类的:


 从上图可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。

好啦,不写了!。。。。




0 0
原创粉丝点击