适配器模式 java demo

适配器模式

适配器设计模式是一种结构设计模式,它通过将一个类的接口转换为客户端期望的另一个类来允许不兼容的接口协同工作。

它在以下情况下特别有用:

您正在与与当前界面不匹配的旧系统或第三方库集成。

您希望在不修改其源代码的情况下重用现有功能。

您需要弥合新旧代码之间的差距,或者使用不同接口设计构建的系统之间的差距。

当面对不兼容的接口时,开发人员通常会求助于重写大部分代码或嵌入条件,例如 处理特殊情况。例如,a 可以使用 逻辑来处理现代 和遗留。 if (legacyType)PaymentProcessorif-elseStripeServiceBankTransferAPI

但随着更多不兼容的服务或模块的引入,这种方法很快就会变得混乱、紧密耦合,并违反了开放/封闭原则------使系统难以扩展或重构。

适配器模式通过引入一个位于系统和不兼容组件之间的包装类来解决这个问题 。它将来自您界面的呼叫转换为旧系统或第三方系统可以理解的呼叫,而无需更改任何一端。

让我们通过一个真实世界的示例,看看如何应用适配器模式来无缝集成不兼容的组件并创建更灵活和可维护的架构。

问题:结账流程中的接口不兼容

想象一下,您正在构建电子商务应用程序的结账组件 。

您的结账服务旨在与支付接口配合使用来处理付款。

预期接口

以下是您 希望任何支付提供商遵守的合同:CheckoutService

复制代码
public interface PaymentProcessor {
    void processPayment(double amount, String currency);
    boolean isPaymentSuccessful();
    String getTransactionId();
}

这种抽象使得在不改变任何核心业务逻辑的情况下交换支付提供商变得容易。

您的内部实施

您的团队已经有一个非常适合此界面的内部支付处理器:

复制代码
public class InHousePaymentProcessor implements PaymentProcessor {
    private String transactionId;
    private boolean isPaymentSuccessful;

    @Override
    public void processPayment(double amount, String currency) {
        System.out.println("InHousePaymentProcessor: Processing payment of " + amount + " " + currency);
        // Process payment logic
        transactionId = "TXN_" + System.currentTimeMillis();
        isPaymentSuccessful = true;
        System.out.println("InHousePaymentProcessor: Payment successful. Txn ID: " + this.transactionId);
    }

    @Override
    public boolean isPaymentSuccessful() {
        return isPaymentSuccessful;
    }

    @Override
    public String getTransactionId() {
        return transactionId;
    }
}

您 使用此界面并与内部支付处理器完美配合:CheckoutService

复制代码
public class CheckoutService {
    private PaymentProcessor paymentProcessor;

    public CheckoutService(PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }

    public void checkout(double amount, String currency) {
        System.out.println("CheckoutService: Attempting to process order for $" + amount + " " + currency);
        paymentProcessor.processPayment(amount, currency);
        if (paymentProcessor.isPaymentSuccessful()) {
            System.out.println("CheckoutService: Order successful! Transaction ID: " + paymentProcessor.getTransactionId());
        } else {
            System.out.println("CheckoutService: Order failed. Payment was not successful.");
        }
    }
}

以下是从您的主要电子商务应用程序调用它的方式:

复制代码
public class ECommerceAppV1 {
    public static void main(String[] args) {
        PaymentProcessor processor = new InHousePaymentProcessor();
        CheckoutService checkout = new CheckoutService(processor);
        checkout.checkout(199.99, "USD");
    }
}

一切都很顺利。您已将结账业务逻辑与底层支付实现分离,从而实现未来的灵活性。到目前为止干得好。

现在,管理层提出了一个新要求:与传统第三方支付提供商集成,广泛使用并经过实战考验......但界面完全不同。

以下是该旧版支付类的样子:

复制代码
public class LegacyGateway {
    private long transactionReference;
    private boolean isPaymentSuccessful;

    public void executeTransaction(double totalAmount, String currency) {
        System.out.println("LegacyGateway: Executing transaction for " + currency + " " + totalAmount);
        transactionReference = System.nanoTime();
        isPaymentSuccessful = true;
        System.out.println("LegacyGateway: Transaction executed successfully. Txn ID: " + transactionReference);
    }

    public boolean checkStatus(long transactionReference) {
        System.out.println("LegacyGateway: Checking status for ref: " + transactionReference);
        return isPaymentSuccessful;
    }

    public long getReferenceNumber() {
        return transactionReference;
    }
}

您现在有两个不兼容的接口。您现有 的期望是一个 .但 没有实现它,它的方法和签名不匹配:CheckoutServicePaymentProcessorLegacyGateway

processPayment(double)与。executeTransaction(double, String)

isPaymentSuccessful()与。checkStatus(long)

getTransactionId() vs. (而且它们的类型也不同!getReferenceNumber()

挑战是这样的:

你无法更改 ------它在系统范围内使用并与界面相关联 。CheckoutServicePaymentProcessor

您无法修改 --- 它来自外部供应商。LegacyGateway

但你必须让它们一起工作。

您需要的是一个转换器------一个位于 和 之间的类 ,将不兼容的接口调整为适用于您的系统的接口。CheckoutServiceLegacyGateway

这正是适配器设计模式的作用。

适配器模式

适配器充当不兼容接口与客户端实际期望之间的桥梁。

它允许您的系统保持灵活性、可扩展性和解耦性,而无需修改现有的客户端代码或第三方库。

两种类型的适配器

实现适配器有两种主要方法,具体取决于语言和用例:

    1. 对象适配器(Java 中的首选)
      使用组合:适配器保存对适配器(它包装的对象)的引用。
      允许跨类层次结构的灵活性和重用。
      这是 Java 中最常见和推荐的方法。
    1. 类适配器(在 Java 中很少见)
      使用继承:适配器从目标接口和适配器继承。
      需要多重继承,Java 不支持类。
      更适合 C++ 等语言。

在我们的例子中,我们将使用 Object Adapter 模式来适应 接口。LegacyGatewayPaymentProcessor

类图

目标接口(例如,):客户端代码期望和使用的接口。PaymentProcessor

Adaptee(例如):具有不兼容接口的现有类,需要调整。LegacyGateway

适配器:实现 Target 接口并在内部使用 Adaptee 的类。它将 Target 接口上的调用转换为 Adaptee 接口上的调用。

客户端(例如,):系统中使用 Target 接口的部分。CheckoutService

现实世界的类比

想象一下,您正在从美国前往欧洲。您的笔记本电脑充电器使用 A 型插头(在美国使用),但欧洲墙壁插座需要 C 型插头。

您无法直接插入充电器 - 接口不匹配。

您无需购买新充电器,而是使用旅行插头适配器。该设备接受您的 A 型插头并将其转换为适合欧洲插座的 C 型形状。

您不会修改墙上插座(它类似于第三方 API)。

您无需修改充电器(这就像您现有的业务逻辑)。

适配器位于中间并转换连接。

对于我们的示例:

充电器→您的应用 (CheckoutService)

墙壁插座→第三方系统(LegacyGateway)

旅行插头适配器 → 适配器等级 (LegacyGatewayAdapter)

您的应用程序需要一个接口 (),但旧系统提供了另一个接口 ()。该适配器允许两者协同工作,而无需改变任何一侧。PaymentProcessorLegacyGateway

实现适配器

为了将旧类集成 到我们的现代电子商务系统中,我们将创建一个 名为 的对象适配器。LegacyGatewayLegacyGatewayAdapter

这个适配器将实现 我们 已经依赖的接口。在内部,它会将方法调用转换为适当的作, 从而有效地弥合不兼容 API 之间的差距。PaymentProcessorCheckoutServiceLegacyGateway

适配器实现

复制代码
public class LegacyGatewayAdapter implements PaymentProcessor {
    private LegacyGateway legacyGateway;

    public LegacyGatewayAdapter(LegacyGateway legacyGateway) {
        this.legacyGateway = legacyGateway;
    }

    @Override
    public void processPayment(double amount, String currency) {
        System.out.println("LegacyGatewayAdapter: Processing payment of " + amount + " " + currency);
        legacyGateway.executeTransaction(amount, currency);
        System.out.println("LegacyGatewayAdapter: Payment processed successfully. Txn ID: " + legacyGateway.getReferenceNumber());
    }

    @Override
    public boolean isPaymentSuccessful() {
        return legacyGateway.checkStatus(legacyGateway.getReferenceNumber());
    }

    @Override
    public String getTransactionId() {
        return String.valueOf(legacyGateway.getReferenceNumber());
    }
}

客户端代码保持不变

适配器模式的美妙之处在于,您的客户端代码仍然完全不知道旧版集成。

不 在乎它是处理现代付款还是传统付款,它总是与 .CheckoutServicePaymentProcessor

更新后的客户端代码如下所示:

复制代码
public class ECommerceAppV2 {
    public static void main(String[] args) {
        PaymentProcessor inHouseProcessor = new InHousePaymentProcessor();
        PaymentProcessor legacyProcessor = new LegacyGatewayAdapter(new LegacyGateway());

        CheckoutService checkoutService = new CheckoutService(inHouseProcessor);
        checkoutService.checkout(100, "USD");

        System.out.println("--------------------------------");

        checkoutService = new CheckoutService(legacyProcessor);
        checkoutService.checkout(100, "USD");
    }
}

是什么让这个适配器工作?

组合胜过继承

我们使用对象组合,而不是继承。适配器包装而不是 子类化它。这使适配器保持:LegacyGateway

松散耦合

更易于测试

更灵活地改变

它还遵循有效的 Java 最佳实践来处理第三方或遗留代码。

方法翻译

中的每个方法 都转换为对旧版 API 的等效调用。这通常包括:PaymentProcessor

重命名或重新映射方法名称

重新组织参数

转换返回类型 --- 例如,将 事务引用转换为格式化 的 IDlongString

旧逻辑的封装

适配器保护代码库的其余部分免受旧类的怪癖或结构的影响。从外部看,没有人知道或关心遗留系统正在被使用。

这改进了:

封装

代码可读性

可维护性

其他阅读材料:

复制代码
https://pan.baidu.com/s/1c1oQItiA7nZxz8Rnl3STpw?pwd=yftc
https://pan.quark.cn/s/dec9e4868381
相关推荐
你的人类朋友36 分钟前
【Docker】说说卷挂载与绑定挂载
后端·docker·容器
蓝莓味的口香糖1 小时前
【JS】什么是单例模式
开发语言·javascript·单例模式
间彧1 小时前
在高并发场景下,如何平衡QPS和TPS的监控资源消耗?
后端
间彧1 小时前
QPS和TPS的区别,在实际项目中,如何准确测量和监控QPS和TPS?
后端
zizisuo1 小时前
解决在使用Lombok时maven install 找不到符号的问题
java·数据库·maven
间彧1 小时前
消息队列(RocketMQ、RabbitMQ、Kafka、ActiveMQ)对比与选型指南
后端·消息队列
笨蛋少年派2 小时前
JAVA基础语法
java·开发语言
Haooog2 小时前
654.最大二叉树(二叉树算法)
java·数据结构·算法·leetcode·二叉树
我真的是大笨蛋2 小时前
依赖倒置原则(DIP)
java·设计模式·性能优化·依赖倒置原则·设计规范
brzhang2 小时前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构