探索设计模式的魅力:深入了解适配器模式-优雅地解决接口不匹配问题


设计模式专栏:http://t.csdnimg.cn/nolNS


目录

一、引言

1. 概述

2. 为什么需要适配器模式

3. 本文的目的和结构

二、简价

1. 适配器模式的定义和特点

  定义

  特点

2. 适配器模式的作用和适用场景

  作用

  适用场景

3. 适配器模式与其他设计模式的比较

三、适配器模式的实现方式

1. 类适配器模式

  结构图

  实现结构

  示例代码

2. 对象适配器模式

  结构图

  实现结构

  示例代码

3. 接口适配器模式

  结构图

  实现结构

  示例代码

四、适配器模式的应用场景

1. 类适配器模式

2. 对象适配器模式

3. 接口适配器模式

五、实战案例

1. 类适配器模式

  实现

  代码示例

2. 对象适配器模式

  实现

  代码示例

3. 接口适配器模式

  实现

  代码示例

六、适配器模式的优缺点和选择

1. 类适配器模式

  优点

  缺点

2. 对象适配器模式

  优点

  缺点

3. 接口适配器模式

  优点

缺点

4.选择哪种选配器模式

七、适配器模式与其他设计模式的结合使用

1. 与装饰器模式结合使用,实现动态适配

2. 与工厂模式结合使用,简化对象创建和适配过程

3. 与观察者模式结合使用,实现事件驱动的适配

4. 与策略模式结合使用,根据不同场景选择不同的适配器

八、总结与展望


一、引言

1. 概述

        适配器模式是一种设计模式,用于将一个类的接口转换成客户端所期望的另一个接口,以解决由于接口不兼容或不匹配而无法协同工作的问题。它使得原本无法一起工作的类能够一起工作,提高了代码的灵活性和复用性,降低了系统间的耦合度。适配器模式主要应用于解决不同组件之间的接口不兼容问题,或者在第三方库与现有系统之间进行集成。

        适配器模式有三种实现方式:类适配器模式对象适配器模式接口适配器模式。类适配器模式通过继承目标类来实现接口转换;对象适配器模式通过包装目标对象来实现接口转换;接口适配器模式通过实现目标接口来实现接口转换。

        使用适配器模式需要注意其优缺点。优点包括提高代码的灵活性和复用性,解决不兼容问题等;缺点包括增加代码的复杂性和学习成本,可能引入额外的性能开销等。因此,在使用适配器模式时,需要根据实际需求选择合适的实现方式,并考虑其适用场景和最佳实践。

        适配器模式可以与其他设计模式结合使用,如与装饰器模式结合实现动态适配,与工厂模式结合简化对象创建和适配过程等。在实际应用中,适配器模式广泛应用于系统集成、遗留代码和新系统之间的桥接、多线程环境下的线程安全适配等方面。

2. 为什么需要适配器模式

        适配器模式是一种经典的设计模式,它的主要作用是解决不同接口之间的兼容性问题。以下是一些需要使用适配器模式的情况:

  1. 接口不兼容: 当系统中的某个接口与另一个接口不兼容时,可以使用适配器模式来进行适配,使得原本无法合作的对象能够协同工作。

  2. 旧系统整合: 在现代化系统中,总是要面对要整合旧系统或者第三方组件的情况,由于接口不兼容,需要适配器模式来进行整合。

  3. 功能复用: 适配器模式还可以用于复用一些功能,例如一个类库中的某个功能非常适合你的项目,但是由于接口不兼容,你可以使用适配器模式来使其能够使用在你的项目中。

  4. 解耦合: 适配器模式可以帮助解决系统之间的耦合度问题,当我们通过适配器模式将两个系统连接在一起时,系统之间的耦合度会降低。

  5. 系统拓展: 在系统开发初期可能考虑不周全,后期需要对现有系统进行功能拓展,适配器模式可以使得拓展系统变得更加容易。

        适配器模式能够帮助我们解决接口不兼容的问题,使得原本无法合作的对象能够协同工作。通过适配器模式,我们可以实现系统间的集成、功能复用、解耦合以及系统拓展,提高系统的灵活性和可维护性。因此,在软件设计中,适配器模式是一种非常重要且常用的设计模式。

3. 本文的目的和结构

  • 介绍适配器模式:适配器模式是一种设计模式,它可以将一个类的接口转换成客户端所期望的另一个接口,从而使原本由于接口不兼容而无法协同工作的类能够一起工作。通过介绍适配器模式的概念、特点和适用场景,可以让读者更好地理解这种设计模式。
  • 分析适配器模式的使用场景:适配器模式适用于需要将两个不兼容的接口进行转换的场景。通过分析实际应用中的案例,可以让读者更好地理解适配器模式的使用场景和优势。
  • 阐述适配器模式的实现方式:适配器模式可以通过不同的方式实现,如类适配器、对象适配器等。通过阐述这些实现方式的原理和优缺点,可以让读者更好地掌握适配器模式的实现技巧。
  • 探讨适配器模式的设计原则:在使用适配器模式时,需要遵循一些设计原则,如组合优于继承、优先使用对象适配等。通过探讨这些原则,可以让读者更好地理解适配器模式的设计思想和应用技巧。
  • 总结适配器模式的优缺点:通过总结适配器模式的优点和缺点,可以让读者更加全面地了解这种设计模式,从而在实际应用中选择合适的场景来使用。

        总之,写这篇适配器模式文章的目的是加深对适配器设计模式的理解,同时也希望对你有一点的帮助,希望我们都能掌握适配器模式的使用场景和实现技巧,以及在设计实践中灵活运用适配器模式来解决问题。

        

二、简价

1. 适配器模式的定义和特点

  定义

        将一个类的接又转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

  特点

  1. 封装性: 适配器模式通过封装被适配者的实现细节,隐藏了具体的实现方式,使得客户端与被适配者解耦,降低了系统的复杂性。

  2. 灵活性: 适配器模式可以在不修改现有代码的情况下增加、替换或者重用已有的适配器,使得系统具有更好的可扩展性和可维护性。

  3. 透明性: 适配器模式对客户端是透明的,客户端无需关心具体的适配器实现细节,只需通过目标接口与适配器进行交互。

  4. 单一职责原则: 适配器模式让不兼容的接口在一个单一的适配器中进行适配和转换,每个适配器只负责转换一个特定的接口,符合单一职责原则。

  5. 开闭原则: 适配器模式符合开闭原则,因为在新增适配器时,无需修改现有的客户端代码,只需要添加新的适配器即可。

  6. 性能考量: 适配器模式可在一定程度上引入性能损耗,因为需要进行接口的转换和适配操作。但通常情况下,适配器模式的性能影响是可以接受的,特别是在实现中可以采用缓存等优化措施。

        适配器模式是一种通过适配器来解决接口不兼容问题的设计模式。它具有兼容性、封装性、灵活性、透明性、单一职责原则、开闭原则和性能考量等特点。通过合理应用适配器模式,可以提高系统的灵活性、可维护性和可扩展性。

2. 适配器模式的作用和适用场景

  作用

  1. 解决接口不兼容问题: 当系统中的两个接口不兼容时,适配器模式可以帮助我们在两个不同的接口之间建立联系,使得原本无法合作的对象能够协同工作。

  2. 实现接口转换和适配: 适配器模式可以通过适配器将一个类的接口转换为客户端所期望的接口,从而实现对不兼容接口的适配。

  3. 提供接口的灵活性和可扩展性: 使用适配器模式,可以将适配器作为一个中间层,使系统的组件之间的耦合度降低。当需要引入新的组件时,只需增加一个适配器,而无需修改现有的组件代码。

  4. 功能复用: 适配器模式可以帮助我们复用一些原本不兼容的功能。通过将这些功能封装在适配器中,使其能够在其他系统中使用。

  5. 系统整合: 在现代化系统中,我们常常需要整合旧系统或第三方组件。由于接口不兼容,适配器模式可以帮助我们将不兼容的接口整合在一起。

        适配器模式的作用是通过适配器将不兼容的接口进行适配和转换,使得系统中的不同组件能够协同工作。它提供了灵活性、可扩展性和功能复用的特性,使得系统的设计更加合理和可维护。通过合理应用适配器模式,可以避免修改现有代码,同时实现系统的集成、功能复用和扩展性提升。

  适用场景

  1. 集成旧系统: 当需要集成一个已有的系统或组件,但其接口与现有系统不兼容时,适配器模式可以使用,以便两者能够协同工作。

  2. 整合第三方库: 在使用第三方库或服务时,由于接口不兼容而无法直接对接时,可以使用适配器模式进行适配,使其能够与现有系统协同工作。

  3. 功能复用: 当需要重用已有,但与当前系统不兼容的功能时,适配器模式可以帮助我们将这些功能适配到系统中。

  4. 系统拓展: 当系统功能需要拓展,同时需要与已有系统或外部组件进行交互时,适配器模式可以用来构建新功能与旧功能的桥梁。

  5. 跨平台开发: 在跨平台开发中,不同平台之间可能存在接口不兼容的情况,适配器模式可以帮助我们在不同平台之间实现统一的接口。

  6. 遗留代码修改: 当需要与遗留系统进行交互,但由于接口不兼容而无法直接整合时,适配器模式可以帮助我们与遗留系统进行对接。

  7. 外部数据格式转换: 当需要将外部数据格式转换为本地系统所需的数据格式时,适配器模式可以用来进行数据格式的转换与适配。

        适配器模式适用于需要解决不兼容接口的场景,同时适用于需要整合、扩展、集成或重用功能的情况。通过合理应用适配器模式,可以使系统变得更具灵活性和可扩展性,同时降低系统组件之间的耦合度,提高系统的可维护性和可复用性。

3. 适配器模式与其他设计模式的比较

  1. 适配器模式 vs 装饰器模式: 适配器模式和装饰器模式都可以用于在现有对象上增加新的功能,但它们的目的和使用场景略有不同。适配器模式主要用于解决接口不兼容的问题,通过适配器将不同接口转换为可兼容的接口。而装饰器模式用于动态地为一个对象添加额外的行为,而且不改变其原有接口。所以适配器模式的关注点是接口转换,而装饰器模式的关注点是动态扩展。

  2. 适配器模式 vs 桥接模式: 适配器模式和桥接模式都可以用于解决不同接口之间的兼容性问题,但它们的实现方式和应用场景有所不同。适配器模式将一个接口转换成客户端所期望的接口,以使得原本不兼容的对象能够协同工作。而桥接模式主要用于将抽象部分与其实现部分解耦,使它们可以独立地变化。适配器模式更注重接口转换和兼容性,而桥接模式更注重抽象和实现的分离。

  3. 适配器模式 vs 外观模式: 适配器模式和外观模式都可以用于简化复杂系统的访问,并提供简单的接口给客户端使用。但它们的主要区别在于关注点的不同。适配器模式主要用于解决不兼容接口的问题,通过适配器将一个接口转换为另一个兼容的接口。而外观模式主要用于为复杂系统提供一个简单的接口,集中封装系统的复杂性,使得使用者能够更方便地与系统交互。

        适配器模式与其他设计模式相比,它的主要作用是解决接口不兼容的问题,同时提供接口转换和适配功能。它与装饰器模式、桥接模式和外观模式等其他设计模式在目的、实现方式和应用场景上有所区别。根据具体的需求和设计情况,我们可以选择合适的设计模式来解决问题。

        

三、适配器模式的实现方式

1. 类适配器模式

        类适配器模式是适配器模式的一种实现方式,它通过继承来实现适配器。在类适配器模式中,适配器类继承了适配者类,并实现了目标接口,从而使得适配器类具备了适配者类的功能,并且可以被客户端使用。

  结构图

  实现结构

  1. 目标接口(Target):定义客户所期待的接口,可以是抽象类或接口。适配器通过实现这个接口来完成适配。

  2. 被适配类(Adaptee):需要被适配的类或接口,它定义了客户不需要的接口。

  3. 适配器(Adapter):适配器类继承了被适配类,并实现了目标接口。在适配器类中,通过调用被适配类的方法来实现目标接口中定义的方法。

  示例代码

// 目标接口
interface Target {void request();
}// 被适配类
class Adaptee {void specificRequest() {System.out.println("Adaptee's specific request");}
}// 适配器类
class Adapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();}
}// 客户端代码
public class Client {public static void main(String[] args) {Target target = new Adapter();  // 使用适配器target.request();}
}

        在上面的示例中,Target 是目标接口,Adaptee 是被适配类,Adapter 是适配器类。适配器类 Adapter 继承了被适配类 Adaptee,并实现了目标接口 Target。在适配器类中,通过调用被适配类的方法 specificRequest() 来实现目标接口中定义的方法 request()。最后,在客户端代码中,通过创建适配器对象,并调用目标接口的方法来实现适配功能。

        这就是类适配器模式的基本实现结构,通过继承被适配类和实现目标接口,适配器类能够实现目标接口,并将客户端的请求适配到被适配类的方法上。

2. 对象适配器模式

        对象适配器模式是适配器模式的另一种实现方式,它通过组合关系来实现适配器。在对象适配器模式中,适配器类持有一个适配者类的实例,并实现了目标接口,从而使得适配器类具备了适配者类的功能,并且可以被客户端使用。

  结构图

  实现结构

  1. 目标接口(Target):客户端所期待的接口。
  2. 适配者类(Adaptee):需要被适配的类,即现有系统中已经存在的类。
  3. 适配器类(Adapter):持有适配者类的实例,并实现了目标接口,用于将适配者类的接口转换成客户端所期待的接口。

  示例代码

// 目标接口
interface Target {void request();
}// 被适配类
class Adaptee {void specificRequest() {System.out.println("Adaptee's specific request");}
}// 适配器类
class Adapter implements Target {private Adaptee adaptee;Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}// 客户端代码
public class Client {public static void main(String[] args) {Adaptee adaptee = new Adaptee();Target target = new Adapter(adaptee);  // 使用适配器,传入被适配类的实例target.request();}
}

        在上面的示例中,Target 是目标接口,Adaptee 是被适配类,Adapter 是适配器类。适配器类 Adapter 持有被适配类 Adaptee 的实例,并实现了目标接口 Target。在适配器类中,通过调用被适配类实例的方法 specificRequest() 来实现目标接口中定义的方法 request()。最后,在客户端代码中,先创建被适配类的实例,并将其传递给适配器类的构造方法,然后通过适配器对象来调用目标接口的方法来实现适配功能。

        这就是对象适配器模式的基本实现结构,通过持有被适配类的实例,并实现目标接口,适配器类能够实现目标接口,并将客户端的请求适配到被适配类的方法上。

3. 接口适配器模式

        接口适配器模式是适配器模式的一种变体,也被称为缺省适配器模式。它主要用于解决一个接口中定义过多的方法,而不希望实现全部方法的情况。

        在接口适配器模式中,适配器类实现了目标接口,并提供了一个空的默认实现,然后具体的适配器类继承适配器类,只需要重写感兴趣的方法即可。

  结构图

  实现结构

  1. 目标接口(Target):客户端所期待的接口。可以是一个接口或抽象类。
  2. 适配器类(Adapter):实现了目标接口,并提供了一个空的默认实现。该适配器类可以是抽象类或具体类。
  3. 具体适配器类(ConcreteAdapter):继承适配器类,并重写感兴趣的方法,以实现适配的功能。

  示例代码

// 目标接口
interface Target {void method1();void method2();void method3();
}// 适配器类(提供默认实现)
abstract class Adapter implements Target {public void method1() {}public void method2() {}public void method3() {}
}// 具体适配器类(只重写感兴趣的方法)
class ConcreteAdapter extends Adapter {public void method1() {System.out.println("Method1 of ConcreteAdapter");}
}// 客户端代码
public class Client {public static void main(String[] args) {Target target = new ConcreteAdapter(); // 使用具体适配器类target.method1(); // 调用适配器的方法target.method2(); // 使用适配器提供的默认实现target.method3(); // 使用适配器提供的默认实现}
}

        在这个示例中,由于目标接口定义了多个方法,但客户端只关心其中的一个方法,所以我们通过接口适配器模式来进行适配。Adapter 类提供了目标接口的默认实现,而 ConcreteAdapter 类继承 Adapter 类并重写了感兴趣的方法,从而实现适配。

        这就是接口适配器模式的实现方式,通过提供默认实现和具体适配器的方式来适配目标接口。这种方式可以灵活地选择需要重写的方法,避免了实现全部方法的繁琐。

        

四、适配器模式的应用场景

1. 类适配器模式

  1. 需要将一个类适配到另一个类接口的情况。
  2. 适配者类的方法和目标接口的方法之间具有相似的功能。
  3. 不需要适配者类的子类进行适配。

2. 对象适配器模式

  1. 需要将一个对象适配到另一个类接口的情况。
  2. 适配者类的方法和目标接口的方法之间具有相似的功能。
  3. 需要动态地改变适配者对象。

3. 接口适配器模式

  1. 需要利用已有的接口,并且只想实现其中的一部分方法。
  2. 需要为多个类提供不同的适配接口。
  3. 希望增加一个统一的接口来方便客户端调用。

        

五、实战案例

1. 类适配器模式

  实现

        一个典型的类适配器模式的实战案例是手机充电器的适配器。以德国的欧标充电器和中国的插头插座为例,在德国的欧标充电器无法直接插入中国的插头插座,但是可以通过适配器进行转接。

        在这个例子中,可以采用类适配器模式来实现适配器,具体实现如下:

  • 目标接口(Target):中国的插头插座接口
  • 适配者类(Adaptee):德国的欧标充电器
  • 适配器类(Adapter):继承自Adaptee并实现了Target接口,用于将欧标充电器的接口转换成中国插头插座的接口

  代码示例

// 目标接口
interface ChineseSocket {void chargeWithChineseSocket();
}// 适配者类
class GermanSocket {public void chargeWithGermanSocket() {System.out.println("Charging with German socket");}
}// 适配器类
class SocketAdapter extends GermanSocket implements ChineseSocket {public void chargeWithChineseSocket() {chargeWithGermanSocket(); // 使用德国插头充电}
}// 客户端代码
public class Client {public static void main(String[] args) {ChineseSocket chineseSocket = new SocketAdapter(); // 使用适配器chineseSocket.chargeWithChineseSocket(); // 调用适配器的方法}
}

        在这个案例中,德国的欧标充电器(Adaptee)无法直接插入中国的插头插座(Target),但是通过类适配器模式中的SocketAdapter,德国的欧标充电器的接口被转换成了可以插入中国插头插座的接口,从而实现了适配。

        这个手机充电器的适配器案例典型地展示了类适配器模式的实战应用,通过继承适配者类并实现目标接口,实现了充电器的适配功能,使得德国的欧标充电器可以充电在中国的插头插座上。

2. 对象适配器模式

  实现

        一个典型的对象适配器模式的实战案例是音频播放器的适配器。假设我们有一个基于Windows系统的音频播放器类,其中定义了playAudio()方法用于播放音频文件。现在需要将这个音频播放器适配到基于Mac系统的应用中,同时又不改变原有的音频播放器类。

        在这个例子中,可以使用对象适配器模式来实现适配器,具体实现如下:

  • 目标接口(Target):Mac系统的音频播放器接口
  • 适配者类(Adaptee):Windows系统的音频播放器类
  • 适配器类(Adapter):持有适配者类的实例,并实现了目标接口,用于将Windows音频播放器的方法转换成Mac音频播放器的方法

  代码示例

// 目标接口
interface MacAudioPlayer {void playMacAudio();
}// 适配者类
class WindowsAudioPlayer {public void playAudio() {System.out.println("Playing audio with Windows audio player");}
}// 适配器类
class AudioPlayerAdapter implements MacAudioPlayer {private WindowsAudioPlayer windowsPlayer; // 持有适配者类的实例public AudioPlayerAdapter(WindowsAudioPlayer windowsPlayer) {this.windowsPlayer = windowsPlayer;}public void playMacAudio() {windowsPlayer.playAudio(); // 调用适配者类的方法来实现适配}
}// 客户端代码
public class Client {public static void main(String[] args) {WindowsAudioPlayer windowsPlayer = new WindowsAudioPlayer();MacAudioPlayer macPlayer = new AudioPlayerAdapter(windowsPlayer); // 使用适配器macPlayer.playMacAudio(); // 调用适配器的方法}
}

        在这个案例中,WindowsAudioPlayer 是需要被适配的类,AudioPlayerAdapter 持有了 WindowsAudioPlayer 的实例,并实现了 MacAudioPlayer 接口,从而使得 AudioPlayerAdapter 成为了 MacAudioPlayer 接口的一个具体实现。客户端代码可以通过 MacAudioPlayer 接口来访问适配器的功能,而适配器内部调用了适配者类的方法来实现适配。

        这个音频播放器的适配器案例展示了对象适配器模式的实战应用,通过持有适配者类的实例并实现目标接口,实现了适配器的功能,使得Windows系统的音频播放器可以适配到Mac系统的应用中。

3. 接口适配器模式

  实现

        一个典型的接口适配器模式的实战案例是日历提醒应用的适配器。假设我们有一个日历提醒应用,它可以通过多种方式进行提醒,包括短信、邮件和推送通知等。现在需要将这个日历提醒应用适配到一个新的第三方消息服务提供商,该提供商仅支持通过邮件进行提醒。

        在这个例子中,可以使用接口适配器模式来实现适配器,具体实现如下:

  • 目标接口(Target):第三方消息服务提供商的邮件提醒接口
  • 适配器类(Adapter):实现了目标接口,并提供了适配的功能
  • 具体适配器类(ConcreteAdapter):继承适配器类,并重写感兴趣的方法,以实现适配 

  代码示例

// 目标接口
interface ThirdPartyEmailReminder {void sendEmailReminder();
}// 适配器类(提供适配的功能)
class CalendarReminderAdapter implements ThirdPartyEmailReminder {private CalendarReminder calendarReminder;  // 日历提醒应用的实例public CalendarReminderAdapter(CalendarReminder calendarReminder) {this.calendarReminder = calendarReminder;}public void sendEmailReminder() {// 调用日历提醒应用的邮件提醒方法来实现适配calendarReminder.sendEmailReminder();}
}// 具体适配器类(可选,根据需要选择实现感兴趣的方法)
class CustomCalendarReminderAdapter extends CalendarReminderAdapter {public CustomCalendarReminderAdapter(CalendarReminder calendarReminder) {super(calendarReminder);}// 根据需要重写感兴趣的方法public void sendEmailReminder() {// 添加自定义的适配逻辑System.out.println("Sending email reminder through custom adapter");super.sendEmailReminder();}
}// 日历提醒应用类
class CalendarReminder {public void sendEmailReminder() {System.out.println("Sending email reminder through calendar reminder app");}
}// 客户端代码
public class Client {public static void main(String[] args) {CalendarReminder calendarReminder = new CalendarReminder();ThirdPartyEmailReminder adapter = new CalendarReminderAdapter(calendarReminder); // 使用适配器adapter.sendEmailReminder(); // 调用适配器的方法ThirdPartyEmailReminder customAdapter = new CustomCalendarReminderAdapter(calendarReminder); // 使用具体适配器customAdapter.sendEmailReminder(); // 调用具体适配器的方法}
}

        在这个案例中,日历提醒应用(CalendarReminder)是需要被适配的类,CalendarReminderAdapter 实现了 ThirdPartyEmailReminder 接口并提供了适配的功能,在适配器中将调用日历提醒应用的邮件提醒方法来实现适配。

        这个日历提醒应用的适配器案例展示了接口适配器模式的实战应用,通过实现目标接口并提供适配的功能来实现适配器,从而使得日历提醒应用可以适配到第三方消息服务提供商的邮件提醒接口中。

六、适配器模式的优缺点和选择

1. 类适配器模式

  优点

  1. 结构简单:类适配器模式只需定义一个适配器类,通过继承和实现目标接口的方式很容易对适配者进行适配,结构相对简单。
  2. 单一职责:适配器通过继承适配者类和实现目标接口的方式,将适配的逻辑与适配者类逻辑分离,符合单一职责原则。

  缺点

  1. 无法适配适配者子类:如果适配者类有子类,那么在类适配器模式中无法对适配者及其子类都进行适配,因为适配器类已经继承了适配者类,导致无法适配适配者类的子类。
  2. 多重继承问题:在一些编程语言中,由于类适配器模式需要同时继承适配者类和实现目标接口,因此会出现多重继承的问题,而某些编程语言不支持多重继承。

2. 对象适配器模式

  优点

  1. 灵活性高:对象适配器模式使用组合的方式来引入适配者对象,因此在运行时可以动态地更换适配者对象,实现了更高的灵活性。
  2. 降低耦合: 适配器和适配者类可以相对独立地变化,降低了适配器与适配者之间的耦合度。

  缺点

  1. 无法适配被final修饰的类:如果适配者类被final关键字修饰,那么无法使用对象适配器模式对其进行适配,因为无法继承该类。
  2. 需要额外引入对象:对象适配器模式需要引入适配者对象,可能会导致额外的内存开销,尤其是在大规模使用时。

3. 接口适配器模式

  优点

  1. 灵活性高:接口适配器模式可以根据需要选择性地实现目标接口中的方法,避免了对所有方法的空实现。
  2. 解耦:适配器和被适配者之间通过接口进行通信,使得它们之间的关系更加松散,降低了耦合性。 

缺点

  1. 代码可读性较差:接口适配器模式的实现通常需要定义一个抽象适配器类并提供接口方法的空实现,这可能会导致代码的可读性较差,增加了代码的复杂性。
  2. 过多的实现类:如果目标接口中定义了很多方法,而适配器只需要实现其中的少数方法,那么需要为每个方法都提供一个空实现,可能会导致产生大量的适配器子类,增加了代码的维护成本。

4.选择哪种选配器模式

        选择适配器模式的具体实现方式(类适配器模式、对象适配器模式和接口适配器模式)取决于以下几个因素:

1. 继承关系:如果适配器需要适配的类已经有一个父类,并且适配器需要继承这个父类的行为,那么可以选择类适配器模式。

  • 类适配器模式使用继承来适配目标类和适配者类,因此只适用于适配器类可以同时继承目标类和适配者类的情况。

2. 对象组合:如果适配器需要适配的类是一个接口,或者适配器需要同时适配多个类,或者适配器希望将适配者类对象作为一个独立的成员变量,那么可以选择对象适配器模式。

  • 对象适配器模式使用对象组合来适配目标类和适配者类,因此适用于适配器类需要适配多个类或者实现接口的情况。

3. 接口实现:如果适配器只需要适配某个接口的部分方法,或者希望为适配的接口提供默认实现,那么可以选择接口适配器模式。

  • 接口适配器模式使用抽象类来实现适配器,因此适用于适配器类只需要实现部分方法或者提供默认实现的情况。

        需要注意的是,选择适配器模式的具体实现方式要根据实际需求和情况来确定。在选择的过程中,需要考虑目标类、适配者类以及适配器类之间的关系,以及需要适配的行为等因素。

        

七、适配器模式与其他设计模式的结合使用

1. 与装饰器模式结合使用,实现动态适配

        适配器模式和装饰器模式是两种设计模式,它们可以结合使用来实现动态适配的功能。

适配器模式用于将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而无法合作的类能够一起工作。而装饰器模式用于在不改变原有对象接口的情况下,动态地为对象添加额外的行为。

        当需要动态适配的功能时,可以使用适配器模式来适配不同的接口,并且通过装饰器模式在适配后的对象上添加额外的行为。具体步骤可以如下:

  1. 创建适配器类,实现目标接口,并引入适配者类。
  2. 创建装饰器类,实现目标接口,并包含一个适配器对象作为成员变量。
  3. 在装饰器类的方法中,先调用适配器对象的对应方法进行适配操作,然后再进行额外的行为添加。

  示例代码

// 定义目标接口
interface Target {void request();
}// 定义适配者类
class Adaptee {public void specificRequest() {System.out.println("Adaptee's specific request");}
}// 定义适配器类
class Adapter implements Target {private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}public void request() {adaptee.specificRequest();}
}// 定义装饰器类
class Decorator implements Target {private Target target;public Decorator(Target target) {this.target = target;}public void request() {// 在调用适配器方法之前可以添加额外的行为System.out.println("Decorator adds extra behavior before calling the request method");target.request();// 在调用适配器方法之后可以添加额外的行为System.out.println("Decorator adds extra behavior after calling the request method");}
}// 测试代码
public class Main {public static void main(String[] args) {// 实例化适配者对象Adaptee adaptee = new Adaptee();// 实例化适配器对象,并传入适配者对象Adapter adapter = new Adapter(adaptee);// 实例化装饰器对象,并传入适配器对象Decorator decorator = new Decorator(adapter);// 调用装饰器对象的方法,实现动态适配和添加额外行为decorator.request();}
}

        在这个示例中,Adaptee 类具有一个 specificRequest 方法,而 Target 接口具有一个 request 方法。通过 Adapter 类,将 Adaptee 的 specificRequest 方法适配到 Target 的 request 方法中。然后通过 Decorator 类,可以在调用适配器方法之前和之后添加额外的行为。

        运行这段代码,输出结果将会是:

Decorator adds extra behavior before calling the request method
Adaptee's specific request
Decorator adds extra behavior after calling the request method

        这个例子展示了如何在 Java 中结合适配器模式和装饰器模式来实现动态适配的功能。

2. 与工厂模式结合使用,简化对象创建和适配过程

在适配器模式与工厂模式结合使用时,可以按照以下具体步骤进行实现:

  1. 定义目标接口(Target):目标接口是适配器对象需要实现的接口。它规定了适配器对象要提供的方法。

  2. 定义适配者类(Adaptee):适配者类是已经存在的一个具体类,其中包含了需要被适配的方法。

  3. 定义适配器类(Adapter):适配器类实现了目标接口,并且内部持有一个适配者对象的引用。适配器类将适配者类的方法适配到目标接口的方法中。

  4. 定义工厂接口(Factory):工厂接口是用于创建适配器对象的接口,声明了创建目标对象的方法。

  5. 实现具体的工厂类(具体工厂):具体的工厂类实现了工厂接口,负责创建适配器对象。在工厂类中实例化适配者对象,并将其作为参数传给适配器对象。

  6. 在客户端中使用工厂类创建目标对象并调用方法:在客户端中,通过具体的工厂类创建适配器对象,然后调用其方法。

  示例代码:

// 定义目标接口
interface Target {void request();
}// 定义适配者类
class Adaptee {public void specificRequest() {System.out.println("Adaptee's specific request");}
}// 定义适配器类
class Adapter implements Target {private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}public void request() {adaptee.specificRequest();}
}// 定义工厂接口
interface TargetFactory {Target createTarget();
}// 实现具体的工厂类
class AdapterFactory implements TargetFactory {public Target createTarget() {Adaptee adaptee = new Adaptee();return new Adapter(adaptee);}
}// 测试代码
public class Main {public static void main(String[] args) {// 使用工厂类创建目标对象TargetFactory factory = new AdapterFactory();Target target = factory.createTarget();// 调用目标对象的方法target.request();}
}

        通过以上步骤,我们定义了目标接口、适配者类、适配器类、工厂接口和具体的工厂类,并在客户端中使用工厂类来创建目标对象并调用其方法。从而实现了适配器模式与工厂模式的结合使用。  

3. 与观察者模式结合使用,实现事件驱动的适配

        适配器模式与观察者模式可以结合使用,用于将已有的对象适配为观察者对象。下面是适配器模式与观察者模式结合使用的实现步骤

  1. 定义目标接口(Subject):目标接口是观察者模式中被观察的对象,包含了添加、删除和通知观察者的方法。

  2. 定义适配者类(Adaptee):适配者类是已有的一个具体类,它包含了一些可观察的状态,但其方法和观察者接口不兼容。

  3. 定义适配器类(Adapter):适配器类实现了目标接口,并在内部持有一个适配者对象的引用。适配器类将适配者类的可观察状态通知转化为目标接口中通知观察者的方法调用。

  4. 定义观察者接口(Observer):观察者接口是观察者模式中的观察者对象,包含了接收到通知后的处理方法。

  5. 实现具体的观察者类(具体观察者):具体的观察者类实现了观察者接口,并定义了接收到通知后的处理逻辑。

  6. 在适配器类中维护观察者列表:在适配器类中维护一个观察者列表,用于管理注册的观察者对象。

  7. 在适配器类的方法中转发适配者的通知:适配器类的方法中,将适配者的可观察状态变化通知转发给观察者列表中的每个观察者对象。

  示例代码

import java.util.ArrayList;
import java.util.List;// 定义目标接口
interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 定义适配者类
class Adaptee {private boolean observableState;public void setObservableState(boolean state) {observableState = state;}public boolean getObservableState() {return observableState;}
}// 定义适配器类
class Adapter implements Subject {private Adaptee adaptee;private List<Observer> observers;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;this.observers = new ArrayList<>();}public void registerObserver(Observer observer) {observers.add(observer);}public void removeObserver(Observer observer) {observers.remove(observer);}public void notifyObservers() {boolean state = adaptee.getObservableState();for (Observer observer : observers) {observer.update(state);}}
}// 定义观察者接口
interface Observer {void update(boolean state);
}// 实现具体的观察者类
class ConcreteObserver implements Observer {public void update(boolean state) {System.out.println("Received notification: " + state);}
}// 测试代码
public class Main {public static void main(String[] args) {// 创建适配者对象Adaptee adaptee = new Adaptee();// 创建适配器对象Adapter adapter = new Adapter(adaptee);// 创建观察者对象Observer observer = new ConcreteObserver();// 注册观察者adapter.registerObserver(observer);// 设置适配者对象的可观察状态adaptee.setObservableState(true);// 通知观察者adapter.notifyObservers();}
}

        通过以上步骤,我们定义了目标接口、适配者类、适配器类、观察者接口以及具体的观察者类,并在适配器类中维护了观察者列表。在客户端中,通过创建适配者对象和适配器对象,并注册观察者、设置可观察状态以及调用通知方法,实现适配器模式与观察者模式的结合使用。

4. 与策略模式结合使用,根据不同场景选择不同的适配器

        适配器模式与策略模式结合使用可以通过策略模式选择不同的适配器来实现根据不同场景选择不同的适配器的功能。下面是适配器模式与策略模式结合使用的实现步骤及Java实现示例:

  1. 定义目标接口(Target):目标接口是适配器对象需要实现的接口。

  2. 定义适配者类(Adaptee):适配者类是已经存在的一个具体类,它的方法和目标接口的方法不兼容。

  3. 定义适配器接口(Adapter):适配器接口也是一个接口,它定义了适配器对象的通用方法。

  4. 定义适配器策略接口(AdapterStrategy):适配器策略接口是一个策略模式的接口,它定义了选择适配器的策略方法。

  5. 实现具体的适配器策略类(ConcreteAdapterStrategy):具体的适配器策略类实现了适配器策略接口,并在策略方法中根据场景选择具体的适配器对象。

  6. 在适配器类中实现目标接口和适配器接口:在适配器类中实现目标接口的方法,并在适配器接口的方法中调用具体适配器的方法。

  7. 根据不同的场景选择适配器策略:在具体的适配器策略类中,根据不同的场景选择具体的适配器对象。

  示例代码

// 定义目标接口
interface Target {void request();
}// 定义适配者类
class Adaptee {public void specificRequest() {System.out.println("Adaptee's specific request");}
}// 定义适配器接口
interface Adapter {void specificRequest();
}// 定义适配器策略接口
interface AdapterStrategy {Adapter getAdapter();
}// 定义具体的适配器策略类
class ConcreteAdapterStrategy implements AdapterStrategy {private String scenario;public ConcreteAdapterStrategy(String scenario) {this.scenario = scenario;}public Adapter getAdapter() {Adapter adapter;if (scenario.equals("A")) {adapter = new Adapter() {private Adaptee adaptee = new Adaptee();public void specificRequest() {adaptee.specificRequest();}};} else if (scenario.equals("B")) {adapter = new Adapter() {public void specificRequest() {System.out.println("Custom specific request");}};} else {adapter = null;}return adapter;}
}// 测试代码
public class Main {public static void main(String[] args) {String scenario = "A"; // 可以根据不同的场景选择适配器策略AdapterStrategy strategy = new ConcreteAdapterStrategy(scenario);Adapter adapter = strategy.getAdapter();if (adapter != null) {adapter.specificRequest();}}
}

        

八、总结与展望

  本文的总结

        本文对适配器模式进行了全面深入的探讨,包括其定义、特点、适用场景、实现方式、应用场景、优缺点以及与其他设计模式的结合使用。文章首先介绍了适配器模式的基本概念和重要性,然后详细分析了类适配器模式、对象适配器模式和接口适配器模式的实现方式和特点。接着,通过实战案例进一步阐述了适配器模式在不同场景下的应用。最后,文章总结了适配器模式的优缺点,并探讨了其与其他设计模式的结合使用,从而更好地理解和应用适配器模式。

  对未来研究的建议和展望

随着软件系统的复杂性和多样性不断增加,适配器模式在未来仍将发挥重要作用。针对适配器模式的应用,以下是对未来研究的建议和展望:

  1. 动态适配技术研究:进一步研究如何在运行时动态地适配接口,以满足不断变化的需求。这可能涉及到反射、代理等技术的研究和应用。
  2. 适配器模式与其他设计模式的结合使用:进一步探索适配器模式与其它设计模式(如装饰器模式、工厂模式、观察者模式等)的结合方式,以提高软件系统的灵活性和可扩展性。
  3. 适配器模式在不同领域的应用研究:研究适配器模式在非传统领域(如物联网、人工智能等)的应用,探索其在解决实际问题中的潜力。
  4. 性能与可维护性研究:进一步研究适配器模式在提高软件性能和可维护性方面的作用,以及如何通过优化适配器模式来降低软件开发的成本。
  5. 理论体系研究:深入探讨适配器模式的理论基础,完善其设计原则和方法论,为适配器模式的进一步应用提供指导。
  6. 案例与实践研究:通过更多的实际案例和实践经验,深入挖掘适配器模式的适用场景和最佳实践,促进其在软件开发中的广泛应用。
  7. 教育和培训研究:开展针对适配器模式的教育和培训工作,提高开发人员对适配器模式的认识和应用能力,推动其在软件开发行业的普及和应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/247665.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS 面试 Swift基础题

一、Swift 存储属性和计算属性比较&#xff1a; 存储型属性:用于存储一个常量或者变量 计算型属性: 计算性属性不直接存储值,而是用 get / set 来取值 和 赋值,可以操作其他属性的变化. 计算属性可以用于类、结构体和枚举&#xff0c;存储属性只能用于类和结构体。存储属性可…

认识产品经理 一个合格的产品经理 产品经理分类

目录 一.合格的产品经理 什么是产品 什么是产品经理 合格的产品经理 什么是产品&#xff1f;区别是&#xff1f; 什么是产品经理 合格的产品经理需要关注哪些核心问题&#xff1f; 二.产品经理分类 为什么会有不同类型 都有那些类型 根据不同类型的职责特点规划个人…

Mysql-InnoDB-数据落盘

概念 1 什么是脏页&#xff1f; 对于数据库中页的修改操作&#xff0c;则首先修改在缓冲区中的页&#xff0c;缓冲区中的页与磁盘中的页数据不一致&#xff0c;所以称缓冲区中的页为脏页。 2 脏页什么时候写入磁盘&#xff1f; 脏页以一定的频率将脏页刷新到磁盘上。页从缓冲区…

C/C++编码问题研究

文章目录 一、Unicode字符集与U8/U16/U32编码二、编码1. 占字节数2. ASCII、GB2312、GBK、GB18030 以及 UTF8 的关系3. BOM4. UTF-8的存储实现 三、编译器字符集设置1. GCC语法Example 2. MSVC语法Example 三、wchar_t五、编码转换函数六、代码 & 实践1. UTF8与UTF16、UTF3…

PDF标准详解(一)——PDF文档结构

已经很久没有写博客记录自己学到的一些东西了。但是在过去一年的时间中自己确实又学到了一些东西。一直攒着没有系统化成一篇篇的文章&#xff0c;所以今年的博客打算也是以去年学到的一系列内容为主。通过之前Vim系列教程的启发&#xff0c;我发现还是写一些系列文章对自己的帮…

HCIP寒假第8次作业

第一步把ipv4网络配通 [r1]int g0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/0]int l0 [r1-LoopBack0]ip add 1.1.1.1 32 [r1]ospf 1 router-id 1.1.1.1 [r1-ospf-1]area 0 [r1-ospf-1-area-0.0.0.0]network 0.0.0.0 255.255.255.255[r2]int g…

静态分析Golang语言生成函数调用关系的利器——go-callvis

目录 升级go删除旧版本安装新版本配置环境变量载入环境修改当前环境修改之后进入的环境 分析安装go-callvis分析其他包总结 导出文件总结 清晰主体脉络总结 其他 参考资料 不同于之前分析C语言项目的工具&#xff0c;go-callvis还是很方便使用。只要把两项工作做好就能顺利的使…

MySQL 定位长事务(Identify Long Transactions)

在MySQL的运行中&#xff0c;经常会遇到一些长事务。长事务意味着长时间持有系统资源&#xff0c;这在OLAP系统中很常见&#xff0c;但在OLTP系统中&#xff0c;长事务意味着争用、并发降低&#xff0c;等待。长事务伴随的典型现象就是经常听到开发人员说"xxx表被锁住了……

开发AI软件,构建多用户AIGC系统,实现图文创作及源码交付

在AI技术不断进步的今天&#xff0c;AI软件开发已成为一个热门的领域。而多用户AIGC系统作为AI软件开发的重要项目之一&#xff0c;呈现出极大的潜力和前景。 多用户AIGC系统旨在为用户提供一个全面的图文创作平台&#xff0c;借助AI的力量&#xff0c;使创作过程更加智能化和…

【程序员英语】【美语从头学】初级篇(入门)(笔记)Lesson10(电话会话Ⅱ)

《美语从头学初级入门篇》 注意&#xff1a;被 删除线 划掉的不一定不正确&#xff0c;只是不是标准答案。 文章目录 Lesson 10 Telephone Conversation Ⅱ 电话会话&#xff08;二&#xff09;会话A会话B笔记I would like to do&#xff08;Id like to to do&#xff09;我想…

用navigator.sendBeacon完成网页埋点异步请求记录用户行为,当网页关闭的时候,依然后完美完成接口请求,不会因为浏览器关闭了被中断请求。

代码用例 <template><div :class"$options.name"><el-button type"primary" click"sendBeacon">navigator.sendBeacon 请求埋点接口 发送json对象数据</el-button></div> </template><script> expor…

LC 2846. 边权重均等查询

2846. 边权重均等查询 难度&#xff1a; 困难 题目大意&#xff1a; 现有一棵由 n 个节点组成的无向树&#xff0c;节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi, wi] 表示树中存在一条位于节点 …

ChatGPT4 比 ChatGPT3.5 强在了那里?

刚开始的时候我还在纠结&#xff0c;一个月20 刀的ChatGPT4 &#xff0c;到底值不值这个价钱&#xff1f;使用过后发现&#xff0c;诶嘛真香。因为 GPT4 比 GPT3.5 多了太多功能&#xff0c;特别是识图能力&#xff0c;用好的话效率翻倍。 1. 看图写代码 ChatGPT4 相比 ChatG…

一文读懂Python中的映射

python中的反射功能是由以下四个内置函数提供&#xff1a;hasattr、getattr、setattr、delattr&#xff0c;改四个函数分别用于对对象内部执行&#xff1a;检查是否含有某成员、获取成员、设置成员、删除成员。 获取成员: getattr class Foo:def __init__(self, name, age):se…

利用Knife4j注解实现Java生成接口文档

文章目录 1、简介2、生成文档3、系列注解3.1、Api3.2、ApiResponses和ApiResponse3.3、ApiOperation3.4、Pathyvariable⭐3.5、RequestBody3.6、ApiOperationSupport3.7、ApiImplicitParams 和 ApiImplicitParam3.8、ApiModel3.9、ApiModelProperty ​&#x1f343;作者介绍&am…

PCB的层叠结构介绍

1. PCB 单层板 剖去我们不要的&#xff0c;然后放入电阻 但是铜皮非常脆弱&#xff0c;很容易断&#xff0c;所以我们放在一块木板上 我们把铜皮放在基板上 显而易见基板肯定是绝缘的 此时单层板就诞生了 此时问题就诞生了&#xff0c;铜皮过电流是有电的&#xff0c;很容易触…

基于STM32的以太网通信协议选择与实现

在基于STM32的以太网通信中&#xff0c;主要涉及到选择合适的通信协议和实现对应的功能代码。常见的通信协议包括TCP/IP、UDP、HTTP等&#xff0c;选择合适的协议取决于具体应用需求。以下将介绍在STM32上进行以太网通信时&#xff0c;常用的通信协议选择以及对应功能代码的实现…

快快销ShopMatrix 分销商城多端uniapp可编译5端-代理商收益管理:差价奖励和销售额统计

代理商收益管理是一种针对代理商的利润分配模式&#xff0c;主要通过差价奖励和销售额统计来实现。这种模式的核心思想是通过激励代理商的销售行为&#xff0c;提高代理商的积极性和销售效率&#xff0c;从而实现整个销售网络的增长。 差价奖励是代理商收益管理中的一种常见方…

vp9协议梳理-header头文件

vp9协议梳理-header头文件 本文是对vp9视频码流中header中包含的语法元素的一个分类整理&#xff0c;及其对具体的解码过程的影响的分析。 这里写目录标题 vp9协议梳理-header头文件1. Vp9码流中的header头文件2. profile3. show_existing_frame, frame_to_show_map_idx4. fr…

Mac下查看、配置和使用环境变量

Mac下查看、配置和使用环境变量 一&#xff1a;Mac怎么查看环境变量命令 printenv一&#xff1a;这个命令会一次性列出所有环境变量的键值对&#xff0c;输出格式为&#xff1a; VAR1value1 VAR2value2 ...二&#xff1a; 也可以通过给这个命令加上环境变量名参数&#xff0…