设计模式——适配器设计模式(结构型)

摘要

本文详细介绍了适配器设计模式,包括其定义、核心思想、角色、结构、实现方式、适用场景及实战示例。适配器模式是一种结构型设计模式,通过将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题,提高系统灵活性和可复用性,符合"开闭原则"。文中还探讨了对象适配器和类适配器两种实现方式,以及如何结合策略模式动态选择适配器。

1. 适配器设计模式定义

适配器模式 :将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器就是"中间翻译层 ",用一个类把"不兼容"的类封装起来,让它们看起来"兼容",从而可以被别的类调用。

1.1.1. 适配器模式的核心思想

  • 解决接口不兼容问题:通过适配器,将一个类的接口"包装"成另一个接口,供客户端调用。
  • 提高系统的灵活性和可复用性:客户端无需修改现有代码即可调用新的类或组件。
  • 符合"开闭原则":对扩展开放,对修改关闭。

1.1.2. 📌 核心点总结:

|--------------|-----------------------------------|
| 点位 | 含义 |
| 目标接口 | 客户端期望使用的接口。 |
| 适配者(Adaptee) | 已有的类(接口不兼容) |
| 适配器(Adapter) | 封装 Adaptee,实现 Target 接口,实现"转换"逻辑。 |

1.1.3. 适配器模式的角色

|------------------|---------------------------------------|
| 角色 | 说明 |
| 目标接口 (Target) | 客户端期望的接口。客户端通过这个接口与适配器交互。 |
| 需要适配的类 (Adaptee) | 现有的接口或类,功能符合需求,但接口不兼容。 |
| 适配器 (Adapter) | 实现目标接口,内部持有需要适配的类的实例,并通过调用其方法来实现目标接口。 |

2. 适配器设计模式结构

适配器模式包含如下角色:

  • Target:目标抽象类
  • Adapter:适配器类
  • Adaptee:适配者类
  • Client:客户类

适配器模式有对象适配器和类适配器两种实现:

2.1. 对象适配器:

2.2. 类适配器:

2.3. 时序图

3. 适配器设计模式实现方式

3.1. 对象适配器(Object Adapter)

实现方式:适配器类内部通过组合的方式,持有被适配者类(Adaptee)的实例。

特点

  • 适配器实现目标接口(Target),
  • 内部调用被适配者实例的方法实现目标接口的方法,
  • 适配灵活,适配器和被适配者解耦,
  • 适配器可以适配多个被适配者实例。

结构示例

复制代码
// 目标接口
interface Target {
    void request();
}

// 被适配者
class Adaptee {
    void specificRequest() {
        System.out.println("被适配者的具体请求");
    }
}

// 适配器(对象适配器)
class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        // 通过调用被适配者的方法完成目标接口功能
        adaptee.specificRequest();
    }
}

3.2. 类适配器(Class Adapter)

实现方式:适配器通过继承的方式,同时继承被适配者类(Adaptee),并实现目标接口(Target)。

特点

  • 适配器类是被适配者的子类,
  • 可以直接调用被适配者的受保护或公共方法,
  • 只能适配一个被适配者类(Java单继承限制),
  • 适配器和被适配者耦合度较高。

结构示例

复制代码
// 目标接口
interface Target {
    void request();
}

// 被适配者
class Adaptee {
    void specificRequest() {
        System.out.println("被适配者的具体请求");
    }
}

// 适配器(类适配器)
class Adapter extends Adaptee implements Target {
    @Override
    public void request() {
        // 直接调用父类的方法
        super.specificRequest();
    }
}

3.3. 适配器示例总结

|-----------|--------------|----------------|
| 特性 | 对象适配器 | 类适配器 |
| 适配方式 | 组合(持有被适配者实例) | 继承(直接继承被适配者) |
| 灵活性 | 高,可以动态切换适配对象 | 低,继承限制,固定继承一个类 |
| 耦合度 | 低 | 高 |
| Java单继承限制 | 无限制 | 只能继承一个被适配者类 |

4. 适配器设计模式适合场景

4.1. ✅ 适合使用适配器设计模式的场景

|------------------------|--------------------------------------------------------------------------------------|
| 使用场景 | 说明 |
| 对接多个第三方接口,接口风格不一致 | 比如接多个风控、支付、短信、物流等服务,不同厂商接口差异很大。→ 使用适配器封装不同厂商接口,统一成系统期望的接口。 |
| 封装老旧系统/遗留代码 | 老系统接口风格与新系统不一致,但又不能修改旧代码。→ 使用适配器包装旧接口,提供符合新接口的使用方式。 |
| 统一Controller参数处理逻辑 | Spring MVC 中自定义参数解析器 HandlerMethodArgumentResolver,可以视为一种适配器,将 HTTP 请求参数适配成业务对象。 |
| 消息中间件适配 | Kafka、RabbitMQ、RocketMQ 提供的消息结构不一致,可使用适配器封装统一消费接口。 |
| 统一日志、监控、埋点等系统接入方式 | 不同日志系统(如 Logback、Log4j、ELK)、监控平台(如 Prometheus、SkyWalking)API 不统一,使用适配器将其统一为系统内部日志接口。 |
| 兼容不同规则引擎或插件机制 | 接入 Drools、EasyRules、自研规则引擎,通过适配器统一规则执行接口。 |
| 跨平台资源访问 | 比如统一适配本地文件系统、FTP、OSS、MinIO 等多种文件服务的上传/下载接口。 |

4.2. ❌ 不适合使用适配器设计模式的场景

|-----------------|-----------------------------|
| 场景 | 原因 |
| 接口已经统一,只需调用不同实现 | 用策略模式或 Spring Bean 多实现注入更合适 |
| 功能非常简单,仅调用一行代码 | 直接调用原类,无需适配 |
| 高性能要求场景,不能增加中间层 | 适配器可能增加调用链层次 |
| 适配器维护成本高于直接重构原类 | 如果可控代码建议重构,而不是套一层适配器 |

4.3. 📌 适配器设计模式总结

|----------------|-----------|-----|
| 项目 | 适配器设计模式适用 | 不适用 |
| 是否接口不兼容但需协同工作 | ✅ 是 | ❌ 否 |
| 是否需要复用现有类且不改代码 | ✅ 是 | ❌ 否 |
| 是否频繁变更需求接口 | ❌ 否 | ✅ 是 |
| 是否对性能极度敏感 | ❌ 否 | ✅ 是 |
| 是否适配类与目标接口差异大 | ❌ 否 | ✅ 是 |

5. 适配器设计模式实战示例

背景:风控系统中,有多个第三方风险评分服务接口(接口不统一),需要统一成系统期望的接口供业务调用。使用适配器模式实现不同第三方服务的适配。

5.1. 场景描述

  • 系统需要调用不同第三方风控服务接口(比如:AlphaRiskServiceBetaRiskService),它们方法名、参数不同。
  • 系统定义统一的风控评分接口RiskScoreService,所有第三方服务通过适配器实现该接口。
  • Spring管理适配器bean,业务直接调用统一接口。

5.2. 定义统一风控评分接口(目标接口)

复制代码
public interface RiskScoreService {
    /**
     * 计算用户的风险评分
     * @param userId 用户ID
     * @return 风险评分分数,范围0-100
     */
    int calculateRiskScore(String userId);
}

5.3. 第三方风控服务接口及实现(被适配者)

复制代码
// 第三方A风险服务,接口不统一
public class AlphaRiskService {
    public double getUserRisk(String userId) {
        // 模拟调用第三方接口,返回0.0~1.0的风险概率
        return Math.random();
    }
}

// 第三方B风险服务,接口不同
public class BetaRiskService {
    public int fetchRiskLevel(String userId) {
        // 返回风险等级 1~5,5最高风险
        return (int)(Math.random() * 5) + 1;
    }
}

5.4. 适配器实现统一接口

复制代码
import org.springframework.stereotype.Component;

// Alpha适配器,组合方式(对象适配器)
@Component
public class AlphaRiskAdapter implements RiskScoreService {
    
    @Autowired
    private final AlphaRiskService alphaRiskService;

    @Override
    public int calculateRiskScore(String userId) {
        double riskProb = alphaRiskService.getUserRisk(userId);
        // 将0.0~1.0风险概率转为0~100分
        return (int)(riskProb * 100);
    }
}

// Beta适配器,组合方式
@Component
public class BetaRiskAdapter implements RiskScoreService {

     @Autowired
    private final BetaRiskService betaRiskService;

    @Override
    public int calculateRiskScore(String userId) {
        int riskLevel = betaRiskService.fetchRiskLevel(userId);
        // 将风险等级1~5映射为0~100分
        return (riskLevel - 1) * 25;
    }
}

5.5. 业务服务调用统一接口

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RiskEvaluationService {

    @Autowired
    private final RiskScoreService riskScoreService;

    // 自定义注解@Qualifier来选择注入哪个适配器。
    // @Qualifier("")
    // private final RiskScoreService riskScoreService;

    public void evaluateUserRisk(String userId) {
        int score = riskScoreService.calculateRiskScore(userId);
        System.out.println("用户 " + userId + " 的风险评分为:" + score);
        // 根据风险评分做后续风控策略处理...
    }
}

5.6. Spring配置说明

  • 你可以通过Spring配置或自定义注解@Qualifier来选择注入哪个适配器。
  • 或者用策略模式管理多个适配器,根据业务动态选择。

6. 适配器设计模式思考

6.1. 用策略模式管理多个适配器,根据业务动态选择(策略模式 + 适配器模式结合示例)。

下面给你一个策略模式 + 适配器模式结合的示例,用于风控系统中动态选择不同适配器实现。

6.1.1. 设计思路

  • 各个第三方风控服务适配成实现统一接口 RiskScoreService 的适配器。
  • 定义策略上下文 RiskScoreContext,根据业务传入的标识动态选择具体适配器(策略)执行。
  • Spring管理多个适配器Bean,使用 @Qualifier 或自定义注解区分。
  • 业务调用上下文,动态选择适配器执行。

6.1.2. 统一接口(适配器接口)

复制代码
public interface RiskScoreService {
    int calculateRiskScore(String userId);
}

6.1.3. 2. 两个适配器实现(对象适配器)

复制代码
import org.springframework.stereotype.Component;

@Component("alphaAdapter")
public class AlphaRiskAdapter implements RiskScoreService {
    
    private final AlphaRiskService alphaRiskService = new AlphaRiskService();

    @Override
    public int calculateRiskScore(String userId) {
        double riskProb = alphaRiskService.getUserRisk(userId);
        return (int) (riskProb * 100);
    }
}

@Component("betaAdapter")
public class BetaRiskAdapter implements RiskScoreService {
    
    private final BetaRiskService betaRiskService = new BetaRiskService();

    @Override
    public int calculateRiskScore(String userId) {
        int riskLevel = betaRiskService.fetchRiskLevel(userId);
        return (riskLevel - 1) * 25;
    }
}

6.1.4. 策略上下文类,注入所有适配器

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class RiskScoreContext {

    private final Map<String, RiskScoreService> strategyMap;

    @Autowired
    public RiskScoreContext(Map<String, RiskScoreService> strategyMap) {
        this.strategyMap = strategyMap;
    }

    /**
     * 根据key选择对应的适配器执行
     * @param strategyKey 适配器标识,如 "alphaAdapter"、"betaAdapter"
     * @param userId 用户ID
     * @return 风险评分
     */
    public int calculate(String strategyKey, String userId) {
        RiskScoreService service = strategyMap.get(strategyKey);
        if (service == null) {
            throw new IllegalArgumentException("未知的风险评分策略:" + strategyKey);
        }
        return service.calculateRiskScore(userId);
    }
}

6.1.5. 业务调用示例

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RiskEvaluationService {

    private final RiskScoreContext riskScoreContext;

    @Autowired
    public RiskEvaluationService(RiskScoreContext riskScoreContext) {
        this.riskScoreContext = riskScoreContext;
    }

    public void evaluateUserRisk(String userId, String strategyKey) {
        int score = riskScoreContext.calculate(strategyKey, userId);
        System.out.println("使用策略[" + strategyKey + "],用户" + userId + "风险评分为:" + score);
        // 这里可根据score做风控决策处理
    }
}

6.1.6. 测试调用示例

复制代码
// 假设有Spring Boot主程序启动后,调用如下:

@Autowired
RiskEvaluationService evaluationService;

public void test() {
    evaluationService.evaluateUserRisk("user123", "alphaAdapter");
    evaluationService.evaluateUserRisk("user456", "betaAdapter");
}

6.1.7. 说明

  • Spring会自动将所有实现了RiskScoreService接口的Bean注入到strategyMap中,key为Bean的名称(如alphaAdapterbetaAdapter)。
  • 业务调用时传入策略key,根据key动态选择对应适配器。
  • 这样便实现了"策略模式管理多个适配器,根据业务动态选择"的需求

博文参考

相关推荐
哆啦A梦的口袋呀1 小时前
基于Python学习《Head First设计模式》 第一章 策略模式
python·学习·设计模式
冰茶_18 小时前
适配器模式:让不兼容接口协同工作
microsoft·设计模式·适配器模式
magic 24519 小时前
Java设计模式详解:策略模式(Strategy Pattern)
java·设计模式·策略模式
琢磨先生David19 小时前
从模式到架构:Java 工厂模式的设计哲学与工程化实践
java·设计模式
庄小焱20 小时前
设计模式——状态设计模式(行为型)
设计模式
庄小焱20 小时前
设计模式——责任链设计模式(行为型)
设计模式
勤奋的知更鸟20 小时前
Java抽象工厂模式详解
设计模式
季鸢1 天前
Java设计模式之迭代器模式详解
java·设计模式·迭代器模式
NorthCastle1 天前
设计模式-行为型模式-模版方法模式
java·设计模式·模板方法模式
庄小焱1 天前
设计模式——观察者设计模式(行为型)
设计模式