适配器模式
适配器设计模式是一种结构设计模式,它通过将一个类的接口转换为客户端期望的另一个类来允许不兼容的接口协同工作。
它在以下情况下特别有用:
您正在与与当前界面不匹配的旧系统或第三方库集成。
您希望在不修改其源代码的情况下重用现有功能。
您需要弥合新旧代码之间的差距,或者使用不同接口设计构建的系统之间的差距。
当面对不兼容的接口时,开发人员通常会求助于重写大部分代码或嵌入条件,例如 处理特殊情况。例如,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
这正是适配器设计模式的作用。
适配器模式
适配器充当不兼容接口与客户端实际期望之间的桥梁。
它允许您的系统保持灵活性、可扩展性和解耦性,而无需修改现有的客户端代码或第三方库。
两种类型的适配器
实现适配器有两种主要方法,具体取决于语言和用例:
-
- 对象适配器(Java 中的首选)
使用组合:适配器保存对适配器(它包装的对象)的引用。
允许跨类层次结构的灵活性和重用。
这是 Java 中最常见和推荐的方法。
- 对象适配器(Java 中的首选)
-
- 类适配器(在 Java 中很少见)
使用继承:适配器从目标接口和适配器继承。
需要多重继承,Java 不支持类。
更适合 C++ 等语言。
- 类适配器(在 Java 中很少见)
在我们的例子中,我们将使用 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