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

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

概要

概念

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

组成

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

  • 目标接口(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的扩展了。

总结

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

相关文章
|
6天前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
10天前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
|
8天前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性
设计模式在PHP开发中至关重要,如单例模式确保类仅有一个实例并提供全局访问点,适用于管理数据库连接或日志记录。工厂模式封装对象创建过程,降低系统耦合度;策略模式定义算法系列并使其可互换,便于实现不同算法间的切换。合理选择设计模式需基于需求分析,考虑系统架构,并通过测试驱动开发验证有效性,确保团队协作一致性和代码持续优化。设计模式能显著提升代码质量,解决开发中的设计难题。
24 8
|
5天前
|
设计模式 算法 PHP
PHP中的设计模式:提升代码的灵活性与可维护性
在本文中,我们将深入探讨PHP编程语言中的一种重要概念——设计模式。设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它代表了最佳的实践,被有经验的面向对象的软件开发人员所采用。本文将通过具体的实例,展示如何在PHP项目中应用设计模式,以提高代码的灵活性和可维护性。无论你是PHP初学者还是经验丰富的开发者,都能从中获得有价值的见解。
|
7天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入探索与实践在软件开发的广袤天地中,PHP以其独特的魅力和强大的功能,成为无数开发者手中的得力工具。而在这条充满挑战与机遇的征途上,设计模式犹如一盏明灯,指引着我们穿越代码的迷雾,编写出更加高效、灵活且易于维护的程序。今天,就让我们聚焦于设计模式中的璀璨明珠——策略模式,深入探讨其在PHP中的实现方法及其实际应用价值。
策略模式,这一设计模式的核心在于它为软件设计带来了一种全新的视角和方法。它允许我们在运行时根据不同情况选择最适合的解决方案,从而极大地提高了程序的灵活性和可扩展性。在PHP这门广泛应用的编程语言中,策略模式同样大放异彩,为开发者们提供了丰富的创作空间。本文将从策略模式的基本概念入手,逐步深入到PHP中的实现细节,并通过一个具体的实例来展示其在实际项目中的应用效果。我们还将探讨策略模式的优势以及在实际应用中可能遇到的挑战和解决方案,为PHP开发者提供一份宝贵的参考。
|
7天前
|
设计模式 存储 安全
PHP中的设计模式:单例模式深度解析
在软件开发的广袤天地中,设计模式如同璀璨星辰,指引着程序员们解决复杂问题的方向。其中,单例模式以其独特的魅力,确保了一个类仅有一个实例,并提供了一个访问它的全局访问点,成为众多项目中不可或缺的设计智慧。本文旨在深入探讨PHP中单例模式的实现方式、应用场景及背后的哲理,引导读者思考其在现代软件架构中的重要性与运用策略。
23 5
|
6天前
|
设计模式 存储 数据库连接
探索PHP中的设计模式:提高代码的可维护性与扩展性
本文将深入探讨PHP中常用的设计模式,包括单例模式、工厂模式和观察者模式。通过具体的代码示例,展示如何在实际项目中应用这些设计模式,以提高代码的可维护性与扩展性。无论你是PHP初学者还是有一定经验的开发者,都可以通过本文的学习,提升你的编程技巧和项目架构能力。
|
8天前
|
设计模式 存储 缓存
PHP中的设计模式:单例模式的深入解析
在PHP开发中,设计模式是提高代码可维护性、扩展性和重用性的关键技术之一。本文将深入探讨PHP中的单例模式,包括其定义、实现方式、应用场景以及优缺点。通过对单例模式的全面剖析,帮助开发者更好地理解和应用这一设计模式,从而编写出更加高效和优雅的PHP代码。
|
9天前
|
设计模式 Java 程序员
Java设计模式-适配器模式(8)
Java设计模式-适配器模式(8)
|
9天前
|
设计模式 算法 PHP
PHP中的设计模式:提升代码的可维护性与扩展性
在软件开发中,设计模式是一套经过验证的解决特定问题的模板。对于使用PHP语言的开发者来说,合理运用设计模式不仅可以优化代码结构,还能显著提高项目的可维护性和扩展性。本文将探讨几种常见的设计模式及其在PHP中的应用,帮助开发者编写更高效、更灵活的代码。

推荐镜像

更多
下一篇
无影云桌面