适配器模式
在《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{
@Override
public void gege() {
System.out.println("咯咯叫");
}
@Override
public void fly() {
System.out.println("鸡飞");
}
}
public class YeDuck implements Duck{
@Override
public void gaga() {
System.out.println("嘎嘎叫");
}
@Override
public void fly() {
System.out.println("鸭子飞");
}
}
要想用鸭来冒充鸡,还要一个适配器
public class ChookDuckAdapter implements Chook{
private Duck duck;
public ChookDuckAdapter(Duck duck) {
this.duck = duck;
}
@Override
public void gege() {
duck.gaga();
}
@Override
public 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;
}
@Override
public boolean hasNext() {
return mEnumeration.hasMoreElements();
}
@Override
public T next() {
return mEnumeration.nextElement();
}
@Override
public 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是委派关系,这决定了适配器模式是对象的。
好啦,不写了!。。。。