设计模式之适配器模式:接口对接丝般顺滑(图代码解析面面俱到)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 设计模式之适配器模式:接口对接丝般顺滑(图代码解析面面俱到)

概要

概念

    适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式通过创建一个适配器类,将原始接口转换为目标接口,使得两个不兼容的类可以协同工作。

组成

适配器模式由以下几个主要组件构成:

  • 目标接口(Target ):客户端期望的接口,适配器将原始接口转换为目标接口。
  • 原始接口(Adaptee ):需要被适配的类的接口。
  • 适配器(Adapter):实现目标接口,同时持有一个原始接口的引用,在目标接口方法中调用原始接口方法来完成适配。

类图

工作原理

  1. 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
  2. 从用户的角度看不到被适配者,是解耦的
  3. 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
  4. 用户收到反馈结果,感觉只是和目标接口交互,如图(关键词:src,adapter,dst )

应用场景

    当需要使用一个已存在的类,但其接口与其他类不兼容时,可以使用适配器模式进行接口转换。

    当想要创建一个可复用的类,该类能与多个不相关的类或类层次结构协同工作时,可以使用适配器模式。

优点

1、可以让已存在的类与其他类协同工作,无需重写已有代码。

2、可以将实现细节封装在适配器中,对客户端隐藏具体的实现细节。

3、可以提高代码的可复用性和灵活性,适配器可以用于多种不同的上下文。

类型

类适配器模式

    在类适配器模式中,适配器实现目标接口并继承原始类,通过重写目标方法,调用原始类的方法来完成适配。这种方式需要多重继承支持,方便但局限性较大。

对象适配器模式

    在对象适配器模式中,适配器持有一个原始类的引用,并实现目标接口,目标方法调用原始类的方法来完成适配。这种方式不需要多重继承支持,更加灵活。

两者区别

    类适配器和对象适配器的区别在于它们如何与被适配类(Adaptee)进行连接。

    类适配器使用多重继承,同时继承目标接口(Target)和被适配类(Adaptee)。通过继承的方式,适配器类可以通过调用被适配类的方法来实现目标接口的方法。

    对象适配器使用关联(组合),在适配器类内部持有一个被适配类的实例。适配器类通过调用被适配类实例的方法来实现目标接口的方法。

    总结起来是:类适配器会继承被适配类的接口以及实现,然后将其转换为目标接口;而对象适配器则是利用组合的方式将被适配类的实例嵌入到适配器中,进而实现目标接口

二维表展示:

类适配器 对象适配器
实现方式 使用类的继承关系实现适配 使用对象的组合关系实现适配
关联关系 适配器类同时继承目标接口和被适配者类 适配器持有被适配者对象的引用
可扩展性 不支持适配多个被适配者类 可以适配多个被适配者对象
适配方式 通过继承实现适配,可以重写被适配者的方法 通过组合实现适配,可以调用被适配者的方法
依赖关系 类适配器依赖于被适配者类 对象适配器依赖于被适配者对象

    注意:在C++等支持多重继承的语言中,适配器类可以同时继承目标接口和被适配类,从而实现对两者的继承。而在Java等不支持多重继承的语言中,可以通过实现目标接口和关联被适配类的方式来实现相同的逻辑。

    但是,由于多重继承可能会带来一些问题,如命名冲突、菱形继承等,因此在使用类适配器模式时需要注意继承关系的设计和维护,避免潜在的问题。

    还有一种说法是适配器分成三类,多了一个接口适配器,这三种方式,是根据adeptee在Adapter里的形式来分类,也是命名的由来。

  • 类适配器:在Adapter里,将adeptee当做类,来继承;
  • 对象适配器:在Adapter里,将adeptee作为一个对象,来持有
  • 接口适配器:在Adapter里,将adeptee作为一个接口,来实现

示例代码

    类适配器模式示例(java 单继承,用接口和类模仿继承自两个类的效果)

// 目标接口
interface Target {
    void request();
}
// 原始类
class Adaptee {
    void specificRequest() {
        System.out.println("执行特殊请求");
    }
}
// 类适配器
class ClassAdapter extends Adaptee implements Target {
    public void request() {
        specificRequest();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        Target target = new ClassAdapter();
        target.request();
    }
}

     对象适配器模式示例

// 目标接口
interface Target {
    void request();
}
// 原始类
class Adaptee {
    void specificRequest() {
        System.out.println("执行特殊请求");
    }
}
// 对象适配器
class ObjectAdapter implements Target {
    private Adaptee adaptee;//持有一个被适配类的实例
    ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    public void request() {
        adaptee.specificRequest();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request();
    }
}

实现(对象适配器详解)

    以姚明在NBA打球为例讲解一下适配器模式

业务背景

    在姚明(外籍中锋)加入NBA之处,英文不熟悉,这时候就需要一位翻译在他和教练之间进行沟通。

代码

//抽象球员,包含attack和defense的方法
abstract class Player {
    protected String name;
    public Player(String name){
        this.name=name;
    }
    public Player(){};//空构造为的是表现翻译者的隐身
    public abstract void attack();//进攻
    public abstract void defense();//防守
}
//外籍中锋
public class ForeignCenter {
    private String name;
   public String getName(){
       return this.name;
   }
   public void setName(String name){
       this.name=name;
   }
    public void 进攻(){
        System.out.println("外籍中锋"+name+"进攻");
    }
    public void 防守(){
        System.out.println("外籍中锋"+name+"防守");
    }
}
//翻译(适配器)
public class Translator extends Player {
    private ForeignCenter foreignCenter = new ForeignCenter();
    public Translator(String name) {
        //super(name);
        foreignCenter.setName(name);//给姚明配的翻译,翻译一出生就转配给了姚明
    }
    @Override
    public void attack() {
        foreignCenter.进攻();
    }
    @Override
    public void defense() {
        foreignCenter.防守();
    }
}
//客户端
public class Client {
    public static void main(String[] args) {
        Player center=new Translator("姚明");//翻译隐身
        System.out.println(center.name);//这里会显示null
        center.attack();
        center.defense();//姚明其实听不懂这两句,需要翻译
    }
}

    这里有两个不容易觉察又很重要的地方:

    1、在翻译者类当中,attach和defense两个方法,实际分别调用的都是外籍中锋的进攻和防守方法,这里是用关联的方式实现的逻辑继承,关于这点在后面会细说。

    2、这里做了个小埋伏,让我们看到翻译者的“隐身”

    客户端给翻译者传的名字是姚明,结合业务就是教练叫姚明进攻,姚明听不懂,但是翻译听懂了,翻译再翻译给姚明,“姚明”通过翻译者的构造函数传给谁了呢?看下面的代码

    可以看到这个名字是赋给了外籍中锋,这里我特意在球员类里面添加了一个空的构造函数

    现在来看,客户端中的Translator表面看是Player类型,通过构造函数传进去的名字,也给了ForeignCenter,所以,Center.name就是null,这也是适配器的魅力所在,在无形中解决了接口不相容的问题。

常见问题

为什么有适配器模式

    适配器模式的主要目的是解决两个不兼容的接口之间的兼容性问题。在软件开发过程中,经常会遇到以下情况导致接口不兼容:

    系统需要使用已存在的类,但其接口与系统要求的接口不一致:当我们需要使用某个类的功能,但其接口与我们现有的系统接口不同,无法直接对接,这时候适配器模式可以通过创建适配器来将已存在的类的接口转换为系统要求的接口,从而使这个类能够被系统使用。

    需要复用一些功能类,但这些类的接口与系统接口不兼容:在系统设计过程中可能会存在一些功能优秀的类,我们希望能够将它们复用于系统中,但是由于这些类的接口与系统接口不一致,无法直接复用,这时候适配器模式可以通过创建适配器来将这些类的接口转换为系统接口,从而让它们能够被复用。

    适配器模式的出现可以降低代码的耦合性,使得不兼容的接口能够协调工作。它能够提高代码的可复用性和灵活性,并且将适配过程封装在适配器中,对客户端隐藏了具体的实现细节,符合面向对象设计原则中的封装和抽象原则。

适配器模式告诉我们什么

    在软件设计中尽量避免使用适配器模式,而是在接口设计阶段就考虑兼容性问题并设计好接口。会的目的是不出现这样的问题

    在软件设计中,应该尽可能提前考虑这些因素,并遵循良好的接口设计原则,以避免后期出现兼容性问题。这包括:

  • 接口设计要明确、简洁、易于理解和使用,遵循单一职责原则和接口隔离原则。
  • 合理规划系统的接口,预见可能的变化和需求,避免频繁修改接口。
  • 使用设计模式和设计原则来约束和指导接口设计,例如依赖倒置原则、开闭原则等。

    总之,尽管适配器模式在某些情况下是非常有用的,但在软件设计过程中,应该尽量避免使用适配器模式,而是通过良好的接口设计来解决兼容性问题。这样可以提高代码的可读性、可维护性和可扩展性,减少后期修改和重构的工作量。

适配器模式体现了哪些设计原则

    单一职责原则(Single Responsibility Principle):每个类都只有一个职责,Adaptee 是适配器模式中的已有类,Adapter 是适配器类,Target 是目标接口,它们各自承担不同的职责。适配器类的职责就是进行接口转换,只有一个职责。

    开闭原则(Open-Closed Principle):适配器模式通过引入适配器类,可以在不修改原有代码的情况下,实现与新接口的兼容,符合开闭原则的要求。

    接口隔离原则(Interface Segregation Principle):抽象目标接口 Target 只包含客户端所需的方法,避免了客户端直接依赖于不需要的方法。

关联方式实现了逻辑继承

    Adapter 类通过关联一个 Adaptee 对象来实现适配器功能。当 Adapter 的 request() 方法被调用时,它实际上会调用被适配类 Adaptee 的 specificRequest() 方法。(请配合上面的类图看)。适配器类(Adapter)包含一个被适配的类(Adaptee)的实例,并在目标接口的方法中调用被适配类的相应方法来实现适配。作为一个中间层,适配器类(Adapter)不仅提供了目标接口的实现,还继承了被适配类(Adaptee)的功能,实现了逻辑上的继承关系。当客户端调用适配器类的方法时,实际上是通过适配器类来调用被适配类的方法,这就体现了适配器类对被适配类的逻辑继承。

适配器模式在SpringMVC框架应用

    SpringMvc 中的 HandlerAdapter , 就使用了适配器模式

    使用 HandlerAdapter 的原因:处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用Controller方法,需要调用的时候就得不断是使用if else来进行判断是哪一种子类然后执行。那么如果后面要扩展Controller,就得修改原来的代码,这样违背了开闭原则。

    Spring 定义了一个适配接口,使得每一种 Controller 有一种对应的适配器实现类,适配器代替controller执行相应的方法,扩展 Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

总结

    适配器模式是一种非常实用的设计模式,通过将不兼容的接口转换为目标接口,使得原本无法协同工作的类能够协作。它能够提高代码的复用性和灵活性,并且将实现细节封装在适配器中,对客户端隐藏具体的实现细节。但是,重点是学习适配器模式的目的是让我们尽量避免使用它,在软件设计中,应该尽可能提前考虑这些因素,并遵循良好的接口设计原则,以避免后期出现兼容性问题。

相关文章
|
3天前
|
数据可视化 前端开发 测试技术
接口测试新选择:Postman替代方案全解析
在软件开发中,接口测试工具至关重要。Postman长期占据主导地位,但随着国产工具的崛起,越来越多开发者转向更适合中国市场的替代方案——Apifox。它不仅支持中英文切换、完全免费不限人数,还具备强大的可视化操作、自动生成文档和API调试功能,极大简化了开发流程。
|
5天前
|
搜索推荐 UED Python
实现一个带有昼夜背景切换的动态时钟:从代码到功能解析
本文介绍了一个使用Python和Tkinter库实现的动态时钟程序,具有昼夜背景切换、指针颜色随机变化及整点和半点报时功能。通过设置不同的背景颜色和随机变换指针颜色,增强视觉吸引力;利用多线程技术确保音频播放不影响主程序运行。该程序结合了Tkinter、Pygame、Pytz等库,提供了一个美观且实用的时间显示工具。欢迎点赞、关注、转发、收藏!
122 94
|
5天前
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
93 11
|
27天前
|
自然语言处理 搜索推荐 数据安全/隐私保护
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
鸿蒙登录页面设计展示了 HarmonyOS 5.0(Next)的未来美学理念,结合科技与艺术,为用户带来视觉盛宴。该页面使用 ArkTS 开发,支持个性化定制和无缝智能设备连接。代码解析涵盖了声明式 UI、状态管理、事件处理及路由导航等关键概念,帮助开发者快速上手 HarmonyOS 应用开发。通过这段代码,开发者可以了解如何构建交互式界面并实现跨设备协同工作,推动智能生态的发展。
144 10
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
|
25天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
25天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
25天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
25天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
25天前
|
设计模式 Java 程序员
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
本系列文章聚焦于面向对象软件设计中的设计模式,旨在帮助开发人员掌握23种经典设计模式及其应用。内容分为三大部分:第一部分介绍设计模式的概念、UML图和软件设计原则;第二部分详细讲解创建型、结构型和行为型模式,并配以代码示例;第三部分通过自定义Spring的IOC功能综合案例,展示如何将常用设计模式应用于实际项目中。通过学习这些内容,读者可以提升编程能力,提高代码的可维护性和复用性。
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
|
1月前
|
设计模式 JSON 前端开发
前端必须掌握的设计模式——适配器模式
适配器模式是一种结构型设计模式,用于使接口不兼容的对象能够相互合作。通过在客户端和系统之间引入一个“中间层”适配器,将不同类型的输入数据转换为系统能处理的标准格式,减轻系统的负担,提高扩展性和可维护性。例如,MacBook的扩展坞将多种接口(如HDMI、USB)转换为Type-C接口,实现多接口兼容。

推荐镜像

更多