一、适配器模式定义
将一个类的接口变换成客户端所期待的另一种接口, 从而使原本因接口不匹配而无法在一起工作的两
个类能够在一起工作;
二、适配器模式的三个角色
1. Target目标角色;
2. Adaptee源角色;
3. Adapter适配器角色;
2.1 Target目标角色
该角色定义把其他类转换为何种接口, 也就是我们的期望接口;
2.2 Adaptee源角色
你想要把谁转换成目标角色, 这个"谁"就是源角色, 它是已经存在的、运行良好的类或对象, 经过适配
器角色的包装, 它会成为目标角色;
2.3 Adapter适配器角色
适配器模式的核心角色, 其他两个角色都是已经存在的角色, 而适配器角色是需要新建立的, 它的职责
非常简单: 把源角色转换为目标角色, 怎么转换? 通过继承或是类关联的方式;
三、通用代码
3.1 类适配器通用代码
// 目标角色
public interface Target {
//目标角色有自己的方法
public void request();
}
目标角色是一个已经在正式运行的角色, 你不可能去修改角色中的方法, 你能做的就是如何去实现接口
中的方法, 而且通常情况下, 目标角色是一个接口或者是一个抽象类, 一般不会是实现类.
// 目标角色的实现类
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("ConcreteTarget->request()");
}
}
// 源角色: 源角色是一个已经存在的类, 它是一个正常的类, 此时我们不可能改变这个类;
public class Adaptee {
public void doSomething() {
System.out.println("Adaptee->doSomething()");
}
}
// 注意在类适配器的情况下, 适配器是如何做的;
// 此时采取的是继承的方式;
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
super.doSomething();
}
}
public class Client {
public static void main(String[] args) {
//原有的业务逻辑
Target target1 = new ConcreteTarget();
target1.request();
//现在增加了适配器角色后的业务逻辑
Target target2 = new Adapter();
target2.request();
}
}
3.2 对象适配器通用代码(使用FutureTask兼容Runnable的伪代码
)
下面代码是对FutureTask支持Callable和Runnable的伪代码, 下文对FutureTask进行了详细的描述;
public class A {
private B b;
public A(B b) {
this.b = b;
}
public void methodA() {
this.b.methodB();
}
}
public class B {
public void methodB() {...}
}
public class C {
public void methodC() {...}
}
接下来我们希望A能够支持C但是对外暴露的接口不变, 该怎么办?
public class A {
private B b;
public A(B b) {
this.b = b;
}
public A(C c) {
CAdapter cadapter = new CAdapter(c);
b = cadapter;
}
public void methodA() {
this.b.methodB();
}
}
public class CAdapter extends B {
private C c;
@Override
public void methodB() {
c.methodC();
}
}
四、适用场景 :
适配器应用的场景只要记住一点就足够了: 你有动机修改一个已经投产中的接口时, 适配器模式可能是最适合你的模式. 比如系统扩展了, 需要使用一个已有或新建立的类, 但这个类又不符合系统的接口, 怎么办? 使用适配器模式;
五、适配器模式的好处 :
1、适配器模式可以让两个没有任何关系的类在一起运行, 只要适配器这个角色能够搞定他们就可以;
2、增加了累的透明性 : 我们访问的Target目标角色, 但是具体的实现都委托给了源角色, 而这些对高层次模块是透明的, 也是它不需要关心的;
3、提高了类的复用度 : 源角色在原有的系统中还是可以正常使用, 而在目标角色中也可以充当新的演员;
4、灵活性非常好 : 某一天突然不想要适配器, 没问题, 删除掉这个适配器就可以了, 其他的代码都不用修改, 基本上就类似一个灵活的构件, 想用就用, 不想用就卸载;
6. 适配器模式在源码中的运用 :
AsyncTask内部使用了FutureTask, 大致流程如下:
public class AsyncTask {
public final AsyncTask<...> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<...> executeOnExecutor(Executor exec, Params... params) {
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {...}
};
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {...}
};
}
}
当Executor执行execute方法时, 会触使mFuture调用其内部的run方法;
public class FutureTask implements RunnableFuture {...}
public interface RunnableFuture extends Runnable, Future {...}
private static abstract class WorkerRunnable implements Callable {...}
表面要调用FutureTask的run(), 但是实际却执行到了WorkerRunnable的call(), FutureTask如何实现Callable与Runnable的关联?
public class FutureTask implements RunnableFuture {
public FutureTask(Callable callable) {
this.callable = callable;
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
}
}
FutureTask构造函数接收两种参数类型: Callable与Runnable类型, 当传入的参数为Runnable类型时;
public class Executors {
public static Callable callable(Runnable task, T result) {
return new RunnableAdapter(task, result);
}
}
private static final class RunnableAdapter implements Callable {
private final Runnable task;
RunnableAdapter(Runnable task) {
this.task = task;
}
public T call() {
task.run();
return result;
}
}
Runnable会被转为RunnableAdapter对象, RunnableAdapter对象持有Runnable的引用, 而RunnableAdapter又实现于Callable;
当Executor.execute(FutureTask)时, 会触发FutureTask的run()的执行 :
public class FutureTask implements RunnableFuture {
public void run() {
Callable c = callable;
if (c != null && state == NEW) {
result = c.call();
}
}
}
实际上指向了Callable的call()方法, 而此时Callable实际指向RunnableAdapter, 最终执行了Runnable的run方法;