介绍
适配器模式(Adapter Pattern) 是一种结构型设计模式,它的核心目的是使接口不兼容的类能够协同工作。适配器模式通过将一个类的接口转换为客户希望的另一个接口,来解决两个已有接口之间不匹配的问题,从而增加它们之间的兼容性。这种模式在需要复用现有类,而又不想修改原有接口或者无法修改原有接口的情况下特别有用。
适配器模式关键角色
- 目标(Target):这是客户所期待的接口,也是适配器转换后需要符合的接口。客户代码通过这个接口与适配器交互。
- 适配者(Adaptee):这是需要被适配的类或接口,拥有特定的接口,但是不符合客户的需求。
- 适配器(Adapter):这个类负责将适配者的接口转换为客户所期待的目标接口。它通过继承或组合的方式来包装适配者,同时实现目标接口,使得原本不兼容的接口可以协同工作。
适配器模式的种类
- 对象适配器(Object Adapter):在这种方式中,适配器包含一个对适配者类的引用,通过委托调用适配者的方法来达到适配的目的。这种方式更加灵活,因为适配器和适配者之间是松耦合的。
- 类适配器(Class Adapter):在这种方式中,适配器继承自适配者类,并实现目标接口。由于Java等语言不支持多重继承,这种模式的使用可能会受到限制,且它使得适配器和适配者之间是紧耦合的。
对象适配器和类适配器
类适配器 -基于继承
//适配的目标
public interface ITarget{
void f1();
void f2();
void fc();
}
//需要被适配的类
public class Adaptee{
public void fa(){...}
public void fb(){...}
public void fc(){...}
}
//适配器类
public class Adaptor extends Adaptee implements ITarget{
//将fa适配到f1中
public void f1(){
super.fa();
}
public void f2(){
//自定义重新实现f2
}
//fc不用实现,可以直接复用。这是和对象适配器最大的不同
}
对象适配器-基于组合
//适配的目标
public interface ITarget{
void f1();
void f2();
void fc();
}
//需要被适配的类
public class Adaptee{
public void fa(){...}
public void fb(){...}
public void fc(){...}
}
public class Adaptor implements ITarget{
private Adaptee adaptee;
//组合被适配的类
public Adaptor(Adaptee adaptee){
this.adaptee=adaptee;
}
//委托给adaptee实现
public void f1(){
adaptee.fa();
}
public void f2(){
//自定义重新实现f2
}
//委托给adaptee实现
public void fc(){
adaptee.fc();
}
}
如何确定使用继承还是组合
- 如果 Adaptee 方法并不多,那么两种方法都可以。
- 如果 Adaptee 的方法很多,但是大部分和 Target 相同,那么推荐使用继承。这样可以减少编码数量
- 如果 Adaptee 的方法很多,但是定义和 Target 不相同,那么推荐使用组合更加的灵活。
适配器模式的五种应用场景
- 封装有缺陷的接口设计:假设外部类包含很多不需要的静态方法,那么可以使用适配器模式对其进行重新封装得到更易用、更易测试的接口。
- 统一多个同种功能的类的接口设计 例如使用多个敏感词过滤系统过滤敏感词,对不同的敏感词系统的 API 适配成统一的接口设计。后续只需要对敏感词系统的接口进行依次遍历,就算接口有变动也只需要修改过滤器。而不涉及具体的客户端代码。
- **替换依赖的外部系统 **如果需要将系统 A 的系统替换成另一个外部系统,适配器模式可以减少对代码的改动。对于接口 A 系统 IA 替换成系统 B 时,创建 B 对A 的适配器,适配成 A 后替换原本使用 IA 的地方即可。
- 兼容老版本接口 对于老版本的功能接口,升级后可能有更优的实现。但是如果直接删除则会导致老项目无法正常运行,这时将新版本中的老接口的实现改为组合新对象的适配器实现则可以避免强制升级,又能享受新版本实现带来的好处。
适配器模式在 Java 日志系统中的应用
上图是目前 JAVA 目前各个日志系统的示意图,可以看到桥接包的数量比日志产品和日志门面接口还多。
JAVA 日志系统中由于没有统一的接口,导致各个日志系统互不兼容。后续推出了日志系统的接口后,各个厂家就纷纷对日志产品退出自己的桥接包。这真是应了那句经典理论“没有什么问题是加一层抽象层解决不了的,如果有就再加一层”