适配器模式 Adapter Pattern
1.1 基本介绍
(1)适配器模式将某个类的接口转换成为客户端期望的另一个接口表示,主要的目的是兼容性,让原本应接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper).
(2)适配器模式属于结构型模式
(3)主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
1.2 工作原理
- 适配器模式:将一个类的接口转换成为另一种接口,让原本接口不兼容的类可以兼容。
- 从用户的角度看不到被适配者,是解耦的。
- 用户调用适配器转换出来的目标接口方法,适配器再调用被适配者的相关接口方法。
- 用户收到反馈结果,感觉只是和目标接口交互。
类适配器模式
例子:电脑的电源适配器将(家庭用电)电压220伏特转换为5伏特,然后给电脑供电。
被适配者类:
package com.robin.adapter.classAdapter;
// 被适配者类
public class Voltage220V {
public int outPut220V(){
int srcV = 220;
System.out.println("[初始-家庭家用电压]电源电压:"+srcV+"伏特");
return srcV;
}
}
适配接口:
package com.robin.adapter.classAdapter;
// 适配接口
public interface ICpVoltage5V {
public int outPut();
}
电脑类:
package com.robin.adapter.classAdapter;
public class Computer {
public void charge(ICpVoltage5V iCpVoltage5V){
int i = iCpVoltage5V.outPut();
if (i==5){
System.out.println("[电源适配器]电压为5伏特,可以开始充电使用了!");
}else{
System.out.println("[电源适配器]电压不正常,请检查或者更换电源适配器");
}
}
}
适配器类:
package com.robin.adapter.classAdapter;
// 适配器类
public class CpVoltageAdapter extends Voltage220V implements ICpVoltage5V{
@Override
public int outPut() {
// 因为是继承关系,所以调用其父类的 电源电压输出
int src = outPut220V();
// 对220V电压进行简单转换
int dest = src/44;
return dest;
}
}
测试客户端类:
package com.robin.adapter.classAdapter;
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
// 通过传入其适配器类的对象来进行充电
computer.charge(new CpVoltageAdapter());
// [电源适配器]电压为5伏特,可以开始充电使用了!
}
}
类适配器模式的优点和缺点:
- 缺点:Java是单继承,但再类适配器模式中,适配器类需要继承被适配的类,失去了灵活性。并且被适配者类的方法都会在适配器类中暴露出来,增加了使用的成本。
- 优点:因为适配器类继承了被适配者类,所以适配器类可以根据需求,灵活的重写被适配者类。
对象适配器模式
对象适配器模式针对上面的类适配器模式进行了一些修改,通过合成复用来代替原本的继承关系。
tip:对象适配器模式是适配器模式中常用的一种。
例子:还是上面的电脑电源的适配器问题,进行修改,将其改为对象适配器模式的。
我们只需要将适配器类中取消继承被适配的类,然后在适配器类中提供一个被适配类的成员变量及构造器即可。
被适配者类:
package com.robin.adapter.objectAdapter;
// 被适配者类
public class Voltage220V {
public int outPut220V(){
int srcV = 220;
System.out.println("[初始-家庭家用电压]电源电压:"+srcV+"伏特");
return srcV;
}
}
适配接口:
package com.robin.adapter.objectAdapter;
// 适配接口
public interface ICpVoltage5V {
public int outPut();
}
电脑类:
package com.robin.adapter.objectAdapter;
public class Computer {
public void charge(ICpVoltage5V iCpVoltage5V){
int i = iCpVoltage5V.outPut();
if (i==5){
System.out.println("[电源适配器]电压为5伏特,可以开始充电使用了!");
}else{
System.out.println("[电源适配器]电压不正常,请检查更换电源适配器");
}
}
}
适配器类:
package com.robin.adapter.objectAdapter;
// 适配器类
public class CpVoltageAdapter implements ICpVoltage5V {
// 合成复用 将被适配类聚合到适配器类中
private Voltage220V voltage220V = null;
// 提供被适配类的构造器
public CpVoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
// 重写适配接口中的方法
@Override
public int outPut() {
if (null != voltage220V){
int srcV = voltage220V.outPut220V();
int dstV = srcV/44;
System.out.println("电源电压适配完成,电源电压="+dstV);
return dstV;
}
return -1;
}
}
客户端测试类:
package com.robin.adapter.objectAdapter;
public class Client {
public static void main(String[] args) {
System.out.println("对象适配器模式");
Computer computer = new Computer();
computer.charge(new CpVoltageAdapter(new Voltage220V()));
}
}
对象适配器模式与类适配器模式基本一致,只是通过合成复用代替继承,解决了类适配器产生的继承局限问题,使用成本更低更灵活。
接口适配器模式
接口适配器模式的思想:当不需要全部实现接口提供的方法时,可以先设计一个抽象类实现接口,并为该接口中的每个方法提供一个默认实现(空方法体),那么该抽象类的子类可以有选择的覆盖父类的某些方法来实现需求。
接口适配器模式适用于不想使用一个适配接口中所有方法的情况,按照自己的需求来挑选合适的方法自行实现。
比如,你要去旅游了,每个国家插座插孔和电压都不一样,你带了一个万能转换器(比如:wp-933)
然后各国插座如下:
暂且假定该万能转换插孔只提供一个空的插孔,需要你去自定义实现(我例子举得不是很好…有点牵强)
万能插孔适配接口:
package com.robin.adapter.interfaceadpter;
// 万能插孔适配接口
public interface AllJacks {
// 德国标准
public void germanJack();
// 欧洲标准
public void europeanJack();
// 中国,澳大利亚标准
public void chinaAndOzJack();
// 美国标准
public void usaJack();
//......
}
万能插孔抽象类实现接口中的所有方法,提供空方法体:
package com.robin.adapter.interfaceadpter;
// 抽象类实现万能适配接口,实现全部接口方法,提供空方法体
public abstract class AbsAllJacksAdapter implements AllJacks {
// 德国
@Override
public void germanJack() {
}
// 欧洲
@Override
public void europeanJack() {
}
// 中国和澳大利亚
@Override
public void chinaAndOzJack() {
}
// 美国
@Override
public void usaJack() {
}
}
客户端测试:
package com.robin.adapter.interfaceadpter;
public class Client {
public static void main(String[] args) {
// 中国电脑插孔为三孔,方形
String srcJack = "[三孔]三方形孔";
// 假设我现在旅行去美国
AbsAllJacksAdapter absAllJacksAdapter = new AbsAllJacksAdapter(){
@Override
public void usaJack() {
System.out.println("=============使用万能转化器转换=============");
// 美国电压为 100-130V,插孔为三孔,两方一圆
String destJack = "[三孔]两方一圆孔";
System.out.println("[插孔适配完毕]:"+srcJack+"==>"+destJack);
}
};
absAllJacksAdapter.usaJack();
}
}
小结
适配器模式的三种方式,也其实就是被适配类,如何被适配器类获取使用的(类=>继承,对象=>合成复用,接口)。Adapter适配器模式的最大作用就是将原本不兼容的接口融合在一起工作。