适配器模式(Adapter Pattern)是一种结构型设计模式,它允许一个接口与另一个不兼容的接口协同工作。在适配器模式中,我们创建一个适配器类,该类将不兼容的接口转换为客户端所期望的接口,从而使客户端能够使用原本不兼容的接口。
概念
适配器模式主要解决的是两个接口之间由于不兼容而不能直接在一起工作的问题。通过引入一个适配器类,该类继承或实现客户端所期望的接口,并在内部持有不兼容接口的对象,将客户端的调用转换为对不兼容接口对象的调用。
优缺点
优点:
- 提高复用性:通过适配器,可以将已有的类集成到新的系统中,而无需修改其源代码。
- 提高扩展性:在应用程序开发过程中,可以通过添加新的适配器来支持新的接口。
- 灵活性高:适配器模式使得接口之间的依赖关系变得灵活,可以通过修改适配器来改变接口的依赖关系。
- 符合开闭原则:对扩展开放,对修改关闭。当需要增加新的功能时,只需要增加新的适配器类,而不需要修改已有的代码。
缺点:
- 过多适配器会导致系统结构复杂:如果系统中存在过多的适配器,可能会使系统结构变得复杂,难以理解和维护。
- 性能损耗:由于适配器需要在客户端和被适配者之间进行转换,因此可能会引入一定的性能损耗。
- 滥用适配器模式会导致系统设计紊乱:如果不恰当地使用适配器模式,可能会使系统设计变得混乱,难以理解和维护。
应用场景
- 封装有缺陷的接口设计:当外部系统的接口设计存在缺陷时,可以使用适配器模式对接口进行二次封装,抽象出更好的接口设计。
- 统一多个类的接口设计:当某个功能的实现依赖于多个外部系统或类时,可以使用适配器模式将这些系统的接口适配为统一的接口定义,以便使用多态的特性来复用代码逻辑。
- 替换依赖的外部系统:当需要将项目中依赖的一个外部系统替换为另一个外部系统时,可以使用适配器模式来减少对代码的改动。
- 兼容老版本接口:在做版本升级时,对于要废弃的接口,可以使用适配器模式将其暂时保留,并将内部实现逻辑委托给新的接口实现。
代码实现
首先,我们定义客户端所期望的接口(Target
):
// 客户端所期望的接口 | |
public interface Target { | |
void request(); | |
} |
然后,我们定义一个已有的类(Adaptee
),它的方法并不符合Target
接口的要求:
// 已有的类,其方法不符合Target接口 | |
public class Adaptee { | |
public void specificRequest() { | |
System.out.println("Called specificRequest()"); | |
} | |
} |
接下来,我们创建适配器类(Adapter
),它实现了Target
接口,并在内部持有一个Adaptee
对象。适配器类将request()
方法的调用转换为对Adaptee
对象的specificRequest()
方法的调用:
// 适配器类,将Adaptee的方法与Target接口进行适配 | |
public class Adapter implements Target { | |
private Adaptee adaptee; | |
public Adapter(Adaptee adaptee) { | |
this.adaptee = adaptee; | |
} | |
@Override | |
public void request() { | |
adaptee.specificRequest(); // 调用Adaptee的方法,实现了适配 | |
} | |
} |
最后,我们创建一个客户端类(Client
),它使用适配器来与Target
接口交互:
// 客户端类,使用适配器与Target接口交互 | |
public class Client { | |
public static void main(String[] args) { | |
// 创建Adaptee对象 | |
Adaptee adaptee = new Adaptee(); | |
// 创建适配器对象,并传入Adaptee对象 | |
Target target = new Adapter(adaptee); | |
// 调用Target接口的方法,实际上会调用Adaptee的specificRequest方法 | |
target.request(); | |
} | |
} |
运行Client
类的main
方法,你将看到控制台输出:
Called specificRequest() |
这表明客户端通过适配器成功调用了Adaptee
类的specificRequest()
方法,尽管Adaptee
类最初并不符合Target
接口的要求。这就是适配器模式的一个典型应用,它允许我们将一个类的接口转换为客户端所期望的另一个接口。