古法编程: 适配器模式

五一假期,在深圳图书馆的知识海洋中遨游

需要的东西就在面前,但却不能使用,而短时间又无法改造它,于是我们就想办法适配它

适配器模式

适配器模式(Adapter Pattern)

适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况

适配器模式有两种类型:

  1. 类适配器模式
  2. 对象适配器模式

下图为对象适配器模式,通过组合的方式来适配,个人觉得这种方式比类适配器模式好很多,可能是出于Java单继承父类的限制吧。

鸭子与火鸡场景

在一个动物园管理系统中,我们原本只有鸭子(Duck)的接口和实现。但是有一天,动物园引进了一批火鸡(Turkey),而系统的其他部分都是基于 Duck 接口设计的。

问题: 如何让火鸡能够像鸭子一样被系统使用,而不需要修改现有的客户端代码?

解决方案: 使用适配器模式,创建一个"火鸡适配器",让火鸡可以伪装成鸭子。

代码实现

目标接口:Duck(鸭子)

这是客户端期望使用的接口:

java 复制代码
public interface Duck {
    /**
     * 呱呱叫
     */
    void quack();
    void fly();
}

具体实现:MallardDuck(绿头鸭)

这是原有的鸭子实现:

java 复制代码
/**
 * 绿头鸭
 */
public class MallardDuck implements Duck{
    @Override
    public void quack() {
        System.out.println("呱呱呱...");
    }

    @Override
    public void fly() {
        System.out.println("绿头鸭起飞了...");
    }
}

被适配者接口:Turkey(火鸡)

这是新引进的火鸡接口,与 Duck 接口不兼容:

java 复制代码
/**
 * 火鸡
 */
public interface Turkey {
    /**
     * 咯咯叫
     */
    void gobble();
    void fly();
}

被适配者实现:WildTurkey(野火鸡)

java 复制代码
public class WildTurkey implements Turkey{
    @Override
    public void gobble() {
        System.out.println("咯咯咯...");
    }

    @Override
    public void fly() {
        System.out.println("...");
    }
}

⭐适配器:TurkeyAdapter(火鸡适配器)⭐核心

这是适配器模式的关键:让火鸡适配器实现 Duck 接口,内部持有 Turkey 对象,将 Duck 的方法调用转换为 Turkey 的方法调用:

java 复制代码
@RequiredArgsConstructor
public class TurkeyAdapter implements Duck{
    private final Turkey turkey;

    @Override
    public void quack() {
        turkey.gobble();  // 将鸭子的quack()转换为火鸡的gobble()
    }

    @Override
    public void fly() {
        turkey.fly();     // 飞行方法直接委托
    }
}

关键点:

  • TurkeyAdapter 实现了 Duck 接口,所以它可以被当作鸭子使用
  • 内部持有一个 Turkey 对象(组合关系)
  • Duck 接口的方法调用委托给内部的 Turkey 对象

测试代码

java 复制代码
public class DuckTestDrive {
    public static void main(String[] args) {
        System.out.println("""
                鸭子不够了,临时拿一只火鸡来充数
                """);
        List.of(new MallardDuck(), new TurkeyAdapter(new WildTurkey()))
                .forEach(duck -> {
                    duck.quack();
                    duck.fly();
                    System.out.println("__".repeat(20));
                });
    }
}

运行结果

sh 复制代码
鸭子不够了,临时拿一只火鸡来充数

呱呱呱...
绿头鸭起飞了...
________________________________________
咯咯咯...
野火鸡只能短暂的飞行...
________________________________________

模式分析

参与者

  1. Target(目标接口)Duck - 客户端期望使用的接口
  2. Adaptee(被适配者)Turkey - 需要被适配的现有接口
  3. Adapter(适配器)TurkeyAdapter - 将 Adaptee 转换为 Target
  4. Client(客户端)DuckTestDrive - 使用 Target 接口的代码

工作流程

txt 复制代码
客户端调用 Duck.quack()
    ↓
TurkeyAdapter.quack() 接收调用
    ↓
内部委托给 Turkey.gobble()
    ↓
WildTurkey 执行实际的咯咯叫

小结 适配器模式结构图

这是一个对象适配器模式

sh 复制代码
┌─────────────────────────────────────────────────────────────┐
│                    适配器模式结构图                           │
└─────────────────────────────────────────────────────────────┘

         Client (客户端)
      ┌──────────────────┐
      │  DuckTestDrive   │
      │                  │
      │  使用 Duck 接口   │
      └────────┬─────────┘
               │ uses
               ▼
     ┌─────────────────────┐
     │   <<interface>>     │
     │       Duck          │  ◄── Target (目标接口)
     │                     │
     │  + quack(): void    │
     │  + fly(): void      │
     └─────┬──────────┬────┘
           │          │
  implements│          │ implements
           ▼          ▼
  ┌────────────────┐  ┌──────────────────┐
  │  MallardDuck   │  │  TurkeyAdapter   │  ◄── Adapter (适配器)
  │   (绿头鸭)      │  │  (火鸡适配器)     │
  │                │  │                  │
  │ + quack()      │  │ - turkey: Turkey │  ◄── 组合关系
  │ + fly()        │  │                  │
  └────────────────┘  │ + quack()        │
                      │ + fly()          │
                      └────────┬─────────┘
                               │ delegates
                               ▼
                    ┌─────────────────────┐
                    │   <<interface>>     │
                    │      Turkey         │  ◄── Adaptee (被适配者)
                    │                     │
                    │  + gobble(): void   │
                    │  + fly(): void      │
                    └────────┬────────────┘
                             │
                    implements│
                             ▼
                    ┌──────────────────┐
                    │   WildTurkey     │
                    │   (野火鸡)        │
                    │                  │
                    │ + gobble()       │
                    │ + fly()          │
                    └──────────────────┘

说明:

  • Client 只依赖 Target 接口(Duck),不知道 Adaptee(Turkey)的存在
  • Adapter 实现了 Target 接口,同时持有 Adaptee 的引用
  • AdapterTarget 的方法调用转换为 Adaptee 的方法调用
  • 这样可以在不修改客户端代码的情况下,让 AdapteeTarget 一样工作

与其他模式的区别

模式 目的 特点
适配器 转换接口 让不兼容的接口能一起工作
装饰器 增强功能 动态地给对象添加职责
代理 控制访问 为另一个对象提供代理以控制访问
相关推荐
longxibo2 小时前
【Flowable 7.2 源码深度解析与实战】
java·后端·流程图
norq juox2 小时前
Spring 中集成Hibernate
java·spring·hibernate
咸鱼2.02 小时前
【java入门到放弃】Zookeeper
java·zookeeper
雨辰AI2 小时前
从 MySQL 迁移至人大金仓 V9 完整改造指南|分页 / 函数 / 语法兼容全部解决
java·开发语言·数据库·后端·mysql·政务
阿维的博客日记2 小时前
介绍一下Redisson的看门狗机制
java·redis·缓存
大G的笔记本2 小时前
为什么接口中的变量默认是 public static final(常量)
java
java1234_小锋2 小时前
Spring AI 2.0 开发Java Agent智能体 - stream()方法Flux流式响应输出
java·人工智能·spring
庞轩px2 小时前
第四篇:多级缓存架构——Caffeine + Redis + MySQL 三级协同
java·redis·mysql·读写分离·caffeine·本地缓存
rKWP8gKv72 小时前
单例模式在Java中的7种实现:从懒汉式到静态内部类
java·开发语言·单例模式