【设计模式】深入解析适配者模式(包装器模式)的思想、代码实现、优点和客户端与门面模式的区别

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


适配器模式


HandlerAdapter 在 Spring MVC 中使用了适配器模式。


适配器模式定义


适配器模式,也叫包装器模式。将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间

简单来说就是目标类不能直接使用,通过一个新类进行包装一下,适配调用方法使用。把两个不兼容的接口通过一定的方式使之兼容


比如下面两个接口,本身是不兼容的(参数类型不一样,参数个数不一样等等)。

image-20250414153120525


可以通过适配器的方式,使之兼容

image-20250414153133407


适配器模式角色


角色名称描述
Target目标接口(可以是抽象类或接口),客户希望直接用的接口。
Adaptee适配者,但是与 Target 不兼容。
Adapter适配器类,此模式的核心。通过继承或者引用适配者的对象,把适配者转为目标接口。
Client需要使用适配器的对象。

适配器模式的实现


1. 角色分析


在适配器模式中,通常有以下三个角色:

  • 目标接口(Target Interface):客户端所期望的接口,客户端通过这个接口与适配器进行交互。
  • 适配者类(Adaptee Class):已经存在的类,具有需要被适配的接口,但接口与目标接口不兼容。
  • 适配器类(Adapter Class):将适配者类的接口转换为目标接口,使得客户端可以使用适配者类的功能。

场景:前面学习的slf4j就使用了适配器模式slf4j 提供了一系列打印日志的 API,底层调用的是 log4j 或者 logback 来打日志,我们作为调用者,只需要调用 slf4j 的 API 就行了


2. 代码实现


下面这段代码是一个典型的适配器模式(Adapter Pattern)的实现。

在这里插入图片描述


在适配器模式中,通常有以下三个主要角色:

  1. 客户端:使用适配器的代码,它调用适配器提供的接口。
  2. 适配器:实现了客户端期望的接口,并将调用转发到适配者。
  3. 适配者:现有的类,它提供了需要适配的功能,但接口与客户端不兼容。

客户端代码

客户端是使用适配器的代码,它调用适配器提供的接口。在你的代码中,客户端是 Client 类。

package com.bit.book.adapter;

public class Client {
    public static void main(String[] args) {
        Slf4jApi slf4jApi = new LogbackSlf4jAdapter(new Logback());
        slf4jApi.log("我是日志");
    }
}

目标接口

适配器接口是客户端期望的接口,它定义了客户端可以调用的方法。在你的代码中,适配器接口是 Slf4jApi 接口。

package com.bit.book.adapter;

public interface Slf4jApi {
    void log(String log);
}

适配者

适配者是现有的类,它提供了需要适配的功能,但接口与客户端不兼容。在你的代码中,适配者是 LogbackLog4j 类。

Logback 适配者

package com.bit.book.adapter;

public class Logback {
    void print(String log){
        System.out.println("Logback print: " + log);
    }
}

Log4j 适配者

package com.bit.book.adapter;

public class Log4j {
    void log(String log){
        System.out.println("Log4j log: " + log);
    }
}

适配器

适配器实现了客户端期望的接口,并将调用转发到适配者。在你的代码中,适配器是 LogbackSlf4jAdapterLog4jSlf4jAdapter 类。

LogbackSlf4jAdapter 适配器

package com.bit.book.adapter;

public class LogbackSlf4jAdapter implements Slf4jApi {

    private Logback logback;

    public LogbackSlf4jAdapter(Logback logback) {
        this.logback = logback;
    }

    @Override
    public void log(String log) {
        logback.print("LogbackSlf4jAdapter: " + log);
    }
}

Log4jSlf4jAdapter 适配器

package com.bit.book.adapter;

public class Log4jSlf4jAdapter implements Slf4jApi {

    private Log4j log4j;

    public Log4jSlf4jAdapter(Log4j log4j) {
        this.log4j = log4j;
    }

    @Override
    public void log(String log) {
        log4j.log("Log4jSlf4jAdapter: " + log);
    }
}

通过上述分类,代码的结构更加清晰:

  • 客户端Client 类,使用适配器。
  • 适配器接口Slf4jApi 接口,定义了客户端期望的接口。
  • 适配者LogbackLog4j 类,提供了需要适配的功能。
  • 适配器LogbackSlf4jAdapterLog4jSlf4jAdapter 类,实现了适配器接口并将调用转发到适配者。

这种分类有助于理解适配器模式的各个角色及其职责。


适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口,从而使原本因接口不匹配而无法一起工作的两个类能够协同工作

image-20250416093839445

下面我将详细解释代码中各个类和接口的交互过程。


3. 交互过程


3. 运行时交互过程


交互步骤

我们不需要改变 Logback 的 API,只需要通过适配器转换一下,就可以更换日志框架,保障系统的平稳运行:

在这里插入图片描述


通过适配器模式,代码实现了接口的适配,使得原本不兼容的类能够协同工作,同时保持了代码的灵活性和可扩展性

客户端 LogbackSlf4jAdapter Logback Slf4jApi 适配器模式流程 1. 创建 Logback 对象(适配者) 2. 创建适配器( LogbackSlf4jAdapter ) 传入 Logback 对象 3. Slf4jApi 对象调用 log() 方法 4. 委托调用适配器 5. 转换调用 logback.print() 6. 执行结果 7. 返回结果 8. 完成调用 客户端 LogbackSlf4jAdapter Logback Slf4jApi

4. 适配器模式的优势


优势说明示例(Logback → Slf4j)
解耦客户端和适配者客户端不直接依赖适配者类,而是通过目标接口(如 Slf4jApi)与适配器交互,降低耦合度。客户端只需调用 Slf4jApi.log(),无需关心底层是 Log4jLogback 或者其他类实现的实现。
复用适配者类即使适配者类的接口不兼容(如方法名不同),也能通过适配器复用其功能。Slf4jLog4JAdapterlog() 调用转换为 Logback.print(),复用 Logback 的日志功能。
扩展性更换适配者时只需修改适配器,客户端代码无需变动,符合开闭原则(对扩展开放,对修改关闭)。未来若切换为 Log4j,只需新增 Slf4jLog4jAdapter,客户端代码无需调整。
  • 适配器模式的实现并不在 slf4j-core 中(只定义了 Logger),具体实现是在针对 log4j 的桥接器项目 slf4j-log4j12 中。
  • 设计模式的使用非常灵活,一个项目中通常会含有多种设计模式。

门面模式与适配器模式的区别


门面模式(Facade Pattern)适配器模式(Adapter Pattern)都是结构型设计模式,但它们的核心目的和实现方式有显著区别:


1. 设计目的不同


门面模式

  • 目标:简化复杂子系统的调用,提供统一的高层接口。
  • 场景:当系统有多个子模块或接口,客户端需要与这些模块交互时,门面模式通过一个“门面”类隐藏内部复杂性,让客户端只需与门面交互。
  • 关键点不改变原有接口,而是提供更友好的入口。

适配器模式 `

  • 目标:解决接口不兼容问题,使原本不兼容的类能协同工作。
  • 场景:当需要将一个类的接口转换成客户端期望的另一种接口时(例如:旧系统改造、第三方库集成)。
  • 关键点转换接口形式,适配器会包装原有接口并提供新的接口。

2. 角色差异


  • 门面模式
    • 门面类:提供简化的入口,内部可能调用多个子系统功能。
    • 子系统类:保持原有功能不变,门面不会修改其接口。

  • 适配器模式
    • 适配器类:实现目标接口,并包装一个适配者(Adaptee),在其方法中调用适配者的功能。
    • 适配者:需要被适配的原有接口(可能已存在但不符合需求)。

3. 结构对比


  • 门面模式的类图:
    Client → Facade → [SubSystemA, SubSystemB, SubSystemC]
    
    • 门面直接依赖多个子系统。

  • 适配器模式的类图(对象适配器):
    Client → TargetInterface ← Adapter → Adaptee
    
    • 适配器实现目标接口,内部持有适配者的实例。

4. 本质区别


维度门面模式适配器模式
目的简化调用转换接口
接口变化提供新接口,不修改原有接口将旧接口改造成新接口
调用关系门面调用多个子系统适配器调用被适配者
适用阶段设计初期或重构复杂系统集成已有组件/兼容旧代码

5. 总结


  • 门面模式 当你想 隐藏复杂性,提供一个更简单的入口。
  • 适配器模式 当你要 解决接口不匹配,让两个不兼容的接口能一起工作。
  • 门面是“简化”,适配器是“转换”。

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值