适配器模式用于将一个类的接口转换为客户端所期望的另一个接口,以实现不兼容接口之间的协作。它像电器插头转换器一样,解决接口不匹配的问题。它有三种形式:类适配器通过继承与实现完成适配,对象适配器通过组合与实现进行适配,而接口适配器则为接口提供默认实现以简化适配。无论哪种方式,目标都是使不兼容的接口能够协同工作,提高代码灵活性和可复用性,降低系统间耦合度。适配器模式广泛应用于旧代码改造、系统升级及跨平台开发等场景。
[参见]:
Java设计模式:核心概述(一)
Java设计模式:单例模式之六种实现方式详解(二)
Java设计模式:工厂模式之简单工厂、工厂方法、抽象工厂(三)
Java设计模式:建造者模式之经典与流式的三种实现(四)
目录
- 一、什么是适配器模式
- 角色概念
- 使用场景
- 二、适配器模式的三种形式
- 1. 类适配器模式(Class Adapter Pattern)
- 代码
- 使用场景
- 优点
- 缺点
- 2. 对象适配器模式(Object Adapter Pattern)
- 代码
- 使用场景
- 优点
- 缺点
- 3. 接口适配器模式(Interface Adapter Pattern)
- 代码
- 使用场景
- 优点
- 缺点
- 三、注意事项
- 四、适配器与装饰器模式的不同
一、什么是适配器模式
适配器模式(Adapter Pattern)的核心概念是将一个类的接口转换成客户期望的另一个接口,从而让原本接口不兼容的类能够合作无间。适配器模式通过引入一个适配器类来实现这种转换,适配器类通常持有源类(即被适配的类)的引用,并实现目标接口(即客户期望的接口)。客户端代码通过调用适配器类的方法来间接调用源类的方法,从而实现接口的适配。
角色概念
适配器模式的角色概念:
- 目标接口(Target):客户所期待的接口,也就是适配器需要实现的接口。
- 源类/源接口(Adaptee):已存在的类或者接口,其方法或者行为与客户所期待的接口不兼容,需要适配器来进行适配。
- 适配器(Adapter):适配器是适配器模式的核心,它是一个类,该类将源类/源接口的方法或行为转换为客户所期待的接口的方法或行为。适配器通过持有源类/源接口的引用,并在其实现的目标接口方法中调用源类/源接口的方法来实现适配。
使用场景
- 当你想使用一个已经存在的类,但其接口不符合你的需求时,你可以使用适配器模式。
- 当你想要创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作时,你可以使用适配器模式。例如,在Android开发中,Adapter就是一个典型的适配器模式的应用,它使得ListView等UI组件可以与各种数据源进行协作。
- 当你需要一个统一的输出接口,而输入类型不可预知时,你可以使用适配器模式。这样,你可以通过适配器将各种输入类型转换为统一的输出接口。
二、适配器模式的三种形式
适配器模式主要分三种:类适配器模式、对象适配器模式、接口适配器模式(默认适配器模式)。
下面将给出代码,并简要说明它们的使用场景以及各自的优缺点。
1. 类适配器模式(Class Adapter Pattern)
代码
// 源类(Adaptee)
public class Adaptee {
public void specificRequest() {
System.out.println("源类特有请求");
}
}
// 目标接口(Target)
public interface Target {
void request();
}
// 类适配器(继承源类,实现目标接口)
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest(); // 调用源类的方法
System.out.println("类适配器对请求进行了适配");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
}
}
使用场景
当你想将一个已经存在的类适配到一个不兼容的接口,并且这个类的方法在适配器中都会被用到时,可以使用类适配器模式。
优点
- 适配器可以覆盖源类的方法,提供更灵活的行为控制。
- 代码结构比较简单,只需继承源类和实现目标接口。
缺点
- 由于Java不支持多重继承,如果源类已经是某个类的子类,则无法使用类适配器模式。
- 适配器与源类紧密耦合,源类的任何改变都可能影响适配器。
2. 对象适配器模式(Object Adapter Pattern)
代码
// 源类(Adaptee,与类适配器模式中的相同)
// 目标接口(Target,与类适配器模式中的相同)
// 对象适配器(持有源类的引用,实现目标接口)
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // 调用源类的方法
System.out.println("对象适配器对请求进行了适配");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
使用场景
当你想将一个已经存在的类适配到一个不兼容的接口,但你不想使用继承或者源类的方法在适配器中不会被全部用到时,可以使用对象适配器模式。
优点
- 适配器与源类的关系由继承变为关联,更加灵活。
- 可以适配多个源类到同一个目标接口,只需在适配器中更换源类对象即可。
缺点
- 需要额外创建适配器类来适配源类和目标接口。
- 相比类适配器模式,对象适配器模式的性能可能略低,因为涉及到对象的创建和引用。
3. 接口适配器模式(Interface Adapter Pattern)
代码
// 目标接口(有很多方法)
public interface Target {
void method1();
void method2();
// ... 其他方法
}
// 抽象适配器类(实现目标接口,提供所有方法的默认实现)
public abstract class AbstractAdapter implements Target {
@Override
public void method1() { /* 默认实现 */ }
@Override
public void method2() { /* 默认实现 */ }
// ... 其他方法的默认实现
}
// 具体适配器类(继承抽象适配器类,重写需要的方法)
public class ConcreteAdapter extends AbstractAdapter {
@Override
public void method1() {
// 实现具体功能
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Target adapter = new ConcreteAdapter();
adapter.method1(); // 调用具体功能
// adapter.method2(); 使用默认实现,如果需要的话
}
}
使用场景
当一个接口有很多方法,但客户端只对其中的一部分方法感兴趣,或者想要提供一个只需要实现部分方法的默认实现时,可以使用接口适配器模式。
优点
- 客户端可以只覆盖它感兴趣的方法,对于其他方法可以使用默认实现。
- 减少了实现接口所需的工作量,因为大部分方法已经有了默认实现。
缺点
- 如果需要为多个不相关的接口提供适配器,则可能需要为每个接口都创建一个抽象适配器和具体适配器,增加了系统的复杂性。
- 当接口改变时,可能需要更新抽象适配器以提供新的默认实现。
三、注意事项
- 适配器模式不是在软件设计阶段考虑的问题,而是解决正在服役的项目的问题。也就是说,当你发现已有的类无法满足新的需求时,才考虑使用适配器模式。
- 适配器模式实现了对原有接口的扩展,而不是修改原有接口或原有类的代码。这是设计模式的一个重要原则:开闭原则(对扩展开放,对修改封闭)。
- 适配器模式的使用会增加系统的复杂性和理解难度,因为需要引入额外的适配器类。因此,在使用适配器模式时,需要权衡其带来的好处和增加的复杂性。
- 适配器模式并不一定能解决所有的接口不兼容问题。在某些情况下,可能需要采用其他的设计模式或者重构代码来解决接口不兼容的问题。
四、适配器与装饰器模式的不同
与装饰器模式(Decorator Pattern)相比,适配器模式和装饰器模式都涉及到对类和接口的包装和扩展,但它们的侧重点和目的不同。
1. 侧重点不同:适配器模式主要解决的是接口不兼容的问题,它并不关注对象的功能增强,而是专注于将已有的功能通过适配器类以新的接口形式暴露给客户端。而装饰器模式则侧重于对对象的功能进行扩展和增强,它通常在不改变原有对象的基础上,动态地给对象添加一些额外的职责或行为。
2. 目的不同:适配器模式的主要目的是解决接口不兼容的问题,使得原本无法协同工作的类能够一起工作。而装饰器模式的主要目的是在不改变原有类的基础上,动态地扩展类的功能,增加类的职责。
3. 包装形式不同:适配器模式通常是通过继承或关联关系来实现接口的适配,它可能会改变原有接口的结构和形式。而装饰器模式则是通过组合关系来扩展对象的功能,它通常保持原有接口的稳定性和一致性。
4. 使用场景不同:适配器模式通常用于系统升级、代码重构等场景,当原有系统的接口无法满足新系统的需求时,可以使用适配器模式进行接口的适配。而装饰器模式则常用于需要在运行时动态地给对象添加功能或职责的场景,如Java IO流中的BufferedInputStream、BufferedReader等就是典型的装饰器模式的应用。
综上所述,适配器模式和装饰器模式虽然都涉及到对类和接口的包装和扩展,但它们的侧重点、目的、包装形式和使用场景都有所不同。在实际应用中,应根据具体的需求和场景来选择合适的设计模式。