适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
适配器模式-将一个类的接口,转换成客户期望的另一种接口。适配器让原本接口不兼容的类可以合作无间。
设计背景
中国标准插头(三扁),中国香港使用英式标准插头(品字形插头),当我们使用一台港版的苹果手机充电器英标插头在中国大陆使用时,是不能直接使用的,需要买一个转换器进行转换一下,就可以使用中国大陆的插座给手机充电了。参考以下两张图
模式结构和定义
实现目标:就是客户端Client希望通过这个原有的Target接口,实现被适配对象的新功能
模式中的角色:
目标接口(Target):客户所期待的接口,就是客户现在有的接口,旧接口。目标可以是具体的或抽象的类,也可以是接口。(对应中国大陆插头接口)
需要适配的类(Adaptee):需要适配的类或适配者类,就是需要的接口,需要用旧接口实现新功能的新接口。(对应中国香港插头接口)
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
应用实例
/**
* 中国大陆插座接口
*
* @author shengyong.huang
* @date 2020-06-20
*/
public interface IChinaSocket {
/**
* 插入插座充电
*
* @param chinaPlug 插头对象v
*/
void provideChargingService(IChinaPlug chinaPlug);
}
/**
* 中国大陆插头
*
* @author shengyong.huang
* @date 2020-06-20
*/
public interface IChinaPlug {
/**
* 开始充电
*/
void startCharging();
}
/**
* 公牛插座实现
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class BullSocket implements IChinaSocket {
@Override
public void provideChargingService(IChinaPlug chinaPlug) {
System.out.println("使用公牛插座");
chinaPlug.startCharging();
}
}
/**
* 小米插座实现
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class XiaomiSocket implements IChinaSocket {
@Override
public void provideChargingService(IChinaPlug chinaPlug) {
System.out.println("使用小米插座");
chinaPlug.startCharging();
}
}
/**
* 中国香港插座接口
*
* @author shengyong.huang
* @date 2020-06-20
*/
public interface IChinaHongkongSocket {
/**
* 插入插座充电
*
* @param hongkongPlug 插头对象v
*/
void provideChargingService(IChinaHongkongPlug hongkongPlug);
}
/**
* 中国香港插头接口
*
* @author shengyong.huang
* @date 2020-06-20
*/
public interface IChinaHongkongPlug {
/**
* 接收电源开始充电
*/
void receivePowerSupply();
}
/**
* 飞利浦13A插座实现
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class Panasonic13ASocket implements IChinaHongkongSocket {
@Override
public void provideChargingService(IChinaHongkongPlug hongkongPlug) {
System.out.println("使用飞利浦13A插座");
hongkongPlug.receivePowerSupply();
}
}
/**
* 港版苹果手机充电器
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class ChinaHongkongApplePlug implements IChinaHongkongPlug {
@Override
public void receivePowerSupply() {
System.out.println("港版苹果手机充电器开始充电");
}
}
/**
* 中国大陆插头适配器(对象适配器实现)
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class ChinaPlugAdapter implements IChinaPlug {
/**
* 香港插头
*/
private IChinaHongkongPlug hongkongPlug;
/**
* 构造器中初始化
*
* @param hongkongPlug 香港插头实例
*/
public ChinaPlugAdapter(IChinaHongkongPlug hongkongPlug) {
this.hongkongPlug = hongkongPlug;
}
@Override
public void startCharging() {
// 实际调用的是香港插头充电的方法
hongkongPlug.receivePowerSupply();
}
}
/**
* 测试方法
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class TestMain {
public static void main(String[] args) {
IChinaSocket xiaomiSocket = new XiaomiSocket();
IChinaSocket bullSocket = new BullSocket();
IChinaPlug huaweiPlug = new HuaweiPlug();
// 使用小米插座给华为手机充电器充电
xiaomiSocket.provideChargingService(huaweiPlug);
// 使用公牛插座给华为手机充电器充电
bullSocket.provideChargingService(huaweiPlug);
IChinaHongkongSocket panasonic13ASocket = new Panasonic13ASocket();
IChinaHongkongPlug hongkongApplePlug = new ChinaHongkongApplePlug();
// 使用飞利浦插头给港版苹果手机充电器充电
panasonic13ASocket.provideChargingService(hongkongApplePlug);
// 适配器使用
// 使用适配器将港版苹果充电器转换成中国大陆规格
ChinaPlugAdapter chinaPlugAdapter = new ChinaPlugAdapter(hongkongApplePlug);
// 使用小米插座给港版苹果充电器充电
xiaomiSocket.provideChargingService(chinaPlugAdapter);
// 使用公牛插座给港版苹果充电器充电
bullSocket.provideChargingService(chinaPlugAdapter);
}
}
优点和不足
优点
- 更好的复用性,系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
- 透明、简单客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单更直接1.
- 解耦性将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码
- 符合开放-关闭原则同一个适配器可以把适配者类和它的子类都适配到目标接口;可以为不同的目标接口实现不同的适配器,而不需要修改待适配类
缺点
- 过多的使用适配器,会让系统非常零乱,不易整体进行把握
使用场景
系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
多个组件功能类似,但接口不统一且可能会经常切换时,可使用适配器模式,使得客户端可以以统一的接口使用它们
补充
对象适配器和类适配器的区别:
- 类适配器:Adapter 类继承Adaptee (被适配类),同时实现Target 接口(因为 Java 不支持多继承,所以只能通过接口的方法来实现多继承),在 Client 类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。
- 对象适配器不使用多继承或继承的方式,而是使用直接关联,或者称为委托的方式。
区别
类适配器的重点在于类,是通过构造一个继承Adaptee类来实现适配器的功能;
对象适配器的重点在于对象,是通过在直接包含Adaptee类来实现的,当需要调用特殊功能的时候直接使用Adapter中包含的那个Adaptee对象来调用特殊功能的方法即可。
上面👆的例子使用的是对象适配器
类适配器实例:
/**
* 中国大陆插头适配器(类适配器实现)
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class ChinaPlugAdapter extends ChinaHongkongApplePlug implements IChinaPlug {
// 直接继承港版苹果手机充电器,虽然代码更少了,但是这个适配器只能被港版苹果手机充电器使用了
// 如果有港版的华为手机充电器,还需要再重新写个适配器
@Override
public void startCharging() {
// 实际调用的是港版苹果手机充电器的方法
super.receivePowerSupply();
}
}
对象适配器实例:
/**
* 中国大陆插头适配器(对象适配器实现)
*
* @author shengyong.huang
* @date 2020-06-20
*/
public class ChinaPlugAdapter implements IChinaPlug {
/**
* 香港插头
*/
private IChinaHongkongPlug hongkongPlug;
/**
* 构造器中初始化
*
* @param hongkongPlug 香港插头实例
*/
public ChinaPlugAdapter(IChinaHongkongPlug hongkongPlug) {
this.hongkongPlug = hongkongPlug;
}
@Override
public void startCharging() {
// 实际调用的是香港插头充电的方法
hongkongPlug.receivePowerSupply();
}
}
参考:https://blog.csdn.net/qq_36982160/article/details/79965027