适配器模式实际上是将某个类的结果转换成客户端期望的另一个类的接口表示,让原本因为接口不匹配的不能一起工作的两个类可以协同工作,就像适配器一样。适配器模式主要分三类:类适配器模式,对象适配器模式和接口适配器模式。
对于用户的角度,看不到被适配者,也就是被适配的类是无感知的,从而实现了解耦。用户收到的反馈就是感觉只是和目标接口交互。
1. 类适配器模式 比如说220V电压通过适配器转换成5V可以作为手机的输入。那么被适配者就是220V电源,适配者就是这个5V手机。
我们先看这张uml图表示手机类适配器的整个流程。首先Voltage220V表示电源输出220V。然后显然我们需要一个适配器,那么我们定义一个接口Voltage5V 来作为转换。VoltageAdapter实现了这个接口。然后Phone因为要5V输入,所以依赖了Voltage5V 的接口(实际上就是VoltageAdapter)然后Client客户端调用Phone实现各种功能就行了,同时还需要依赖VoltageAdaper因为很显然,客户端用手机的时候需要调用这个转换电压这个接口。
package com.designpatten.adapter.classadapter;
//被适配的类
public class Voltage220V {
public int output220V(){
int src = 220;
System.out.println("电压= " + src+ " V");
return src;
}
}
适配器的接口:
package com.designpatten.adapter.classadapter;
//适配接口
public interface IVoltage5V {
public int output5V();
}
适配器接口的实现:
package com.designpatten.adapter.classadapter;
//因为这个适配器,既要接收220V输入,所以他可以看成一个类似5V的电源所以要继承Voltage220V
//并且要作为适配器,也就是完成接口IVoltage5V
public class VoltageAdapter extends Voltage220V implements IVoltage5V{
@Override
public int output5V() {
//先获取220V的电源,然后把他转成5V输出
int srcV = output220V();
int dstV = srcV/44;
return dstV;
}
}
手机:
package com.designpatten.adapter.classadapter;
//手机,肯定有一个充电功能
public class Phone {
public void charging(IVoltage5V iVoltage5V){
if(iVoltage5V.output5V() == 5){
System.out.println("电压5V可以充电");
}
else{
System.out.println("电压不匹配无法充电");
}
}
}
客户端:
package com.designpatten.adapter.classadapter;
public class client {
public static void main(String []args){
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
但是类适配器有一个问题:类适配器需要继承src类(也就是voltage220V)这个类,但是继承总归不是最好的办法;并且src类在VoltageAdapter里面会暴露出来,增加使用成本。
那么我们用对象适配器进行修改。原来的适配器类是继承被适配的类然后实现这个接口,但是对象适配器本身并不继承被适配的类,而是持有(聚合)这个类。也就是本身原来要继承的类,用构造器聚合到现在的对象适配器里面。uml类图如下所示:
我们能看出来此uml类图和上面的类适配器的区别:首先就是VoltageAdapter不是继承自Voltage220V了,而是聚合了一个Voltage220V的对象。与此同时,因为Clinet要调用VoltageAdapter对象的构造器,所以Clinet也需要依赖Voltage220V.
我们来看一下这里的代码:
主要变化的就是Voltage Adapter和client了。那么我们看看变化在哪里:
VoltageAdapter:
package com.designpatten.adapter.classadapter;
//因为这个适配器,既要接收220V输入,所以他可以看成一个类似5V的电源所以要继承Voltage220V
//并且要作为适配器,也就是完成接口IVoltage5V
public class VoltageAdapter implements IVoltage5V{
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V){
this.voltage220V = voltage220V;
}
//这里有变化了,是聚合关系,吧voltage220V的对象聚合到这个类里面
@Override
public int output5V() {
int dst = 0;
if(null != voltage220V){
int src = voltage220V.output220V();
System.out.println("使用对象适配器进行转换");
dst = src/44;
System.out.println("适配完成,输出电压为: " + dst);
}
return dst;
}
}
client:
package com.designpatten.adapter.classadapter;
public class client {
public static void main(String []args){
Phone phone = new Phone();
//变成对象适配器了,下面这里有变化
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
第三种是接口适配器模式。当不需要全部实现接口提供的方法的时候可以先设计一个抽象类实现接口,并为此解耦的每一个方法提供一个默认的空方法,抽象类的子类可以有选择性的覆盖父类的某些方法实现自己特定的需求。适用于接口不想使用所有方法的情况。
也就是说,interface4定义了很多种方法,但是我们定义了一个抽象类,只是空实现了这个接口的所有功能。在实际程序中我们可能只对某一个方法感兴趣,所以我只是重写我关心的方法:
package com.designpatten.adapter.interfaceAdapter;
public interface Interface4 {
//假设有一个接口下面有三个功能
public void m1();
public void m2();
public void m3();
}
那么对于AbsAdapter这个类,我将其实现所有的方法,但是所有的方法都是空实现,也就是什么都不做:
package com.designpatten.adapter.interfaceAdapter;
public abstract class AbsAdapter implements Interface4{
//注意下面三个方法都是空实现
@Override
public void m1() {
}
@Override
public void m2() {
}
@Override
public void m3() {
}
}
最后假设client端想只使用m1方法,那么实现如下:
package com.designpatten.adapter.interfaceAdapter;
public class client {
public static void main(String[] args){
//在这里重新实现了自己的m1方法
AbsAdapter absAdapter = new AbsAdapter() {
@Override
public void m1() {
System.out.println("只需要调用m1方法");
}
};
absAdapter.m1();
}
}
也就是说只是重新写了一个m1适合自己使用即可,这样方便拓展。