适配器模式的意图在于:使用不同的接口的类所提供的服务为客户端提供他所期望的接口。
接口适配(类的适配)
类图如下:
Client类需要实现Interface_1中的requestMethod方法,但是也需要使用Client2中的usefulMethod方法,这时候要对Client2进行适配,满足客户端Client对象的需要,编写一个继承Clent2并且实现Interface_1的类,通过重写requestMethod方法将客户端的请求委派给usefulMethod方法。这时候NewClient类就解决了Client的需求。
下面来看另一个例子:
PhysicalRocket类拥有仿真器需要的信息,但它的方法并不完全匹配RocketSim接口中仿真功能。主要差异在于仿真器内部保留了一个内部时钟,它不时的会调用setSimTime()方法更新仿真对象。若要适配PyysicalRocket类以满足仿真器类的需要,CompleteRocket对象可以维持一个实例变量,用来传递PhysicalRocket类需要的方法。
CompleteRocket适配器类只是简单的将调用转为使用超类拥有的方法。
public class CompleteRocket extends PhysicalRocket implements RocketSim {
private double time;
public CompleteRocket(double burnArea, double burnRate, double fuelMass,
double totalMass) {
super(burnArea, burnRate, fuelMass, totalMass);
}
/**
* 聚集
* @author 付玉伟
* @time 2015-2-4 下午9:38:28
* @param t
* @return
*/
public double getMass() {
return getMass(time);
}
/**
* 推力
* @author 付玉伟
* @time 2015-2-4 下午9:38:37
* @param t
* @return
*/
public double getThrust() {
return getThrust(time);
}
public double setSimTime(double time) {
return 0;
}
}
类与对象适配器
上述设计通过子类进行适配,在类的适配器类实现需要的接口,并继承自实现的类。但是当你需要的一组方法中并不在接口中,以上方法将行不通,此时可以创建一个对象的适配器,它使用了委派而非继承。
SkyRocket类使用了火箭的基本模型,假如你希望添加一些更复杂的物理模型,而该模型由PhysicalRocket2类使用,为了适配PhysicalRocket2满足系统要求,需要创建OozinozSkyRocket类作为对象适配器,它继承自SkyRocket,同时使用PhysicalRocket2对象。
类图如下:
public class OozinozSkyRocket extends SkyRocket {
private PhysicalRocket2 rocket;
public OozinozSkyRocket(PhysicalRocket2 r) {
super(r.getMass(0), r.getThrust(0), r.getBurnTime());
rocket = r;
}
public double getMass(){
return rocket.getMass(simTime);
}
public double getThrust(){
return rocket.getThrust(simTime);
}
}
此种适配比类的适配更加脆弱:
没有OozinozSkyRocket所提供的接口规范,由于SkyRocket类的变化可能出现编译未检测到的问题
OozinozSkyRocket类需要借助超类的setTime,我们无法保证该变量总是被声明为protected,很难约束他们想要做的事情。
小结
适配器模式可以让我们重用现有的类,以满足客户端的要求。当客户通过接口实现了表达其需求时,通常可以创建一个实现了该接口的新类,同时继承现有的类,这种方式叫做类的适配器。