当前位置: 首页 > news >正文

java设计模式五、适配器模式

文章比如视频好,推荐大家去某站的生生大佬的手写,讲的很清晰

什么是适配器模式

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间进行协作。简单来说,适配器模式就像是一个"转换器",在两个不兼容的系统之间架起一座桥梁,让它们能够正常通信。

生活中的适配器例子

想象一下这样的场景:你有一台只支持USB接口的电脑,但你的充电器却是Type-C接口。这时候你就需要一个转换器:

Type-C接口 → 转换器 → USB接口

这个转换器就是现实生活中的适配器,它让两个原本不兼容的设备能够正常工作。

适配器模式的核心思想

适配器模式主要包含三个角色:

  1. 目标接口(Target):客户端期望的接口
  2. 适配器(Adapter):实现目标接口,并持有被适配者的引用
  3. 被适配者(Adaptee):需要被适配的现有接口

简单示例:Type-C转USB适配器

让我们通过一个具体的代码示例来理解适配器模式的工作原理。

接口定义

首先定义两种不同的接口:

package com.YA33.desgin.adapter;/*** Type-C接口定义* 这是现代设备常用的接口标准*/
public interface TypeCSocket {/*** Type-C连接方法*/void connectTypeC();
}/*** USB接口定义* 这是传统设备常用的接口标准*/
public interface UsbSocket {/*** USB连接方法*/void connectUsb();
}

具体设备实现

package com.YA33.desgin.adapter;/*** MacBook设备,只支持Type-C接口*/
public class MacBook implements TypeCSocket {@Overridepublic void connectTypeC() {System.out.println("MacBook的Type-C接口已连接");}
}/*** 充电器设备,只支持USB接口*/
public class Charger {/*** 充电方法,只能接受USB接口* @param usbSocket USB接口*/public void charge(UsbSocket usbSocket) {usbSocket.connectUsb();System.out.println("开始使用USB接口充电");}
}

核心:适配器实现

package com.YA33.desgin.adapter;/*** Type-C转USB适配器* 这是适配器模式的核心,它实现了目标接口(UsbSocket)* 并持有被适配者(TypeCSocket)的引用*/
public class TypeC2UsbAdapter implements UsbSocket {private final TypeCSocket typeCSocket;/*** 构造函数,传入需要适配的Type-C设备* @param typeCSocket Type-C设备*/public TypeC2UsbAdapter(TypeCSocket typeCSocket) {this.typeCSocket = typeCSocket;}@Overridepublic void connectUsb() {// 调用被适配者的方法typeCSocket.connectTypeC();System.out.println("适配器:正在将Type-C信号转换为USB信号");System.out.println("适配器:转换完成,调用USB接口");// 这里可以添加复杂的转换逻辑// 比如电压转换、数据格式转换等}
}

使用适配器

package com.YA33.desgin.adapter;/*** 主程序,演示适配器模式的使用*/
public class Main {public static void main(String[] args) {// 创建MacBook(Type-C接口)MacBook macBook = new MacBook();// 创建充电器(USB接口)Charger charger = new Charger();// 创建适配器,将Type-C转换为USBTypeC2UsbAdapter adapter = new TypeC2UsbAdapter(macBook);// 通过适配器让充电器给MacBook充电charger.charge(adapter);}
}

运行结果:

MacBook的Type-C接口已连接
适配器:正在将Type-C信号转换为USB信号
适配器:转换完成,调用USB接口
开始使用USB接口充电

复杂示例:Etcd适配Redis模板

在实际的企业级开发中,我们经常会遇到需要将新系统集成到现有框架中的情况。下面是一个更复杂的示例:将Etcd键值存储适配到Spring的Redis模板接口。

场景背景

假设我们有一个现有的Spring Boot应用,它使用Redis作为缓存,代码中大量使用了RedisTemplate。现在由于某些原因(比如性能、成本等),我们想要将存储后端从Redis切换到Etcd,但是不希望修改现有的业务代码。

项目结构

src/main/java/com/YA33/desgin/adapter/spring/
├── controller/
│   └── KVController.java          # 现有的业务控制器
├── etcd/
│   ├── EtcdProperties.java        # Etcd配置属性
│   ├── EtcdRedisConfiguration.java # 配置类
│   ├── EtcdRedisTemplateAdaptor.java # Redis模板适配器
│   ├── EtcdValueOperations.java   # 值操作适配器
│   └── VoidRedisKeyValueAdapter.java # 空适配器
└── AdapterApplication.java        # 启动类

详细代码实现

1. 业务控制器(现有代码)
package com.YA33.desgin.adapter.spring.controller;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;/*** 键值对控制器* 这是现有的业务代码,使用RedisTemplate进行操作* 我们希望通过适配器模式,在不修改这些代码的情况下切换到底层的Etcd*/
@RestController
@RequestMapping("/redis")
public class KVController {private static final Logger log = LoggerFactory.getLogger(KVController.class);private final RedisTemplate<String, String> redisTemplate;public KVController(RedisTemplate<String, String> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 获取键对应的值* @param key 键* @return 值*/@GetMapping("/get/{key}")public String getValue(@PathVariable("key") String key) {log.info("获取键值对,key: {}", key);return redisTemplate.opsForValue().get(key);}/*** 设置键值对* @param key 键* @param value 值* @return 操作结果*/@PutMapping("/put/{key}/{value}")public String putValue(@PathVariable("key") String key, @PathVariable("value") String value) {log.info("设置键值对,key: {}, value: {}", key, value);redisTemplate.opsForValue().set(key, value);return "SUCCESS";}
}
2. Etcd配置属性
package com.YA33.desgin.adapter.spring.etcd;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** Etcd配置属性类* 用于从application.yml中读取Etcd的连接配置*/
@Data
@ConfigurationProperties(prefix = "ya33.etcd")
public class EtcdProperties {/*** Etcd服务地址* 例如: http://localhost:2379*/private String url;
}
3. 配置类
package com.YA33.desgin.adapter.spring.etcd;import io.etcd.jetcd.Client;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisKeyValueAdapter;
import org.springframework.data.redis.core.RedisTemplate;/*** Etcd配置类* 负责创建Etcd客户端和相关的Bean*/
@Configuration
@EnableConfigurationProperties(EtcdProperties.class)
public class EtcdRedisConfiguration {/*** 创建Etcd客户端* @param etcdProperties Etcd配置属性* @return Etcd客户端实例*/@Beanpublic Client etcdClient(EtcdProperties etcdProperties) {return Client.builder().endpoints(etcdProperties.getUrl()).build();}/*** 创建Etcd值操作实例* @param client Etcd客户端* @return 值操作实例*/@Beanpublic EtcdValueOperations operations(Client client) {return new EtcdValueOperations(client);}/*** 创建Redis模板适配器* 这是适配器模式的核心,用Etcd实现RedisTemplate的接口* @param operations Etcd值操作* @return Redis模板适配器*/@Beanpublic RedisTemplate<String, String> redisTemplate(EtcdValueOperations operations) {return new EtcdRedisTemplateAdaptor(operations);}/*** 创建空的Redis键值适配器* 用于满足Spring Data Redis的依赖* @return 空适配器*/@Beanpublic RedisKeyValueAdapter redisKeyValueAdapter() {return new VoidRedisKeyValueAdapter();}
}
4. Redis模板适配器
package com.YA33.desgin.adapter.spring.etcd;import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;/*** Redis模板适配器* 继承RedisTemplate,但底层使用Etcd进行操作*/
public class EtcdRedisTemplateAdaptor extends RedisTemplate<String, String> {private final EtcdValueOperations operations;/*** 构造函数* @param operations Etcd值操作实例*/public EtcdRedisTemplateAdaptor(EtcdValueOperations operations) {this.operations = operations;// 设置值操作器setValueOperations(operations);}@Overridepublic ValueOperations<String, String> opsForValue() {return operations;}@Overridepublic void afterPropertiesSet() {// 重写此方法,避免父类的初始化逻辑// 因为我们的适配器不需要Redis连接工厂等组件}
}
5. Etcd值操作适配器
package com.YA33.desgin.adapter.spring.etcd;import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.kv.GetResponse;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ValueOperations;import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;/*** Etcd值操作适配器* 实现ValueOperations接口,但底层使用Etcd客户端* 这是适配器模式中最复杂的部分,需要适配所有的方法*/
public class EtcdValueOperations implements ValueOperations<String, String> {private final Client client;public EtcdValueOperations(Client client) {this.client = client;}/*** 设置键值对 - 核心方法* 将Redis的set操作适配到Etcd的put操作*/@Overridepublic void set(String key, String value) {ByteSequence etcdKey = ByteSequence.from(key, StandardCharsets.UTF_8);ByteSequence etcdValue = ByteSequence.from(value, StandardCharsets.UTF_8);try {// 调用Etcd客户端的put方法client.getKVClient().put(etcdKey, etcdValue).get();} catch (Exception ex) {throw new RuntimeException("Etcd设置键值对失败", ex);}}/*** 获取值 - 核心方法* 将Redis的get操作适配到Etcd的get操作*/@Overridepublic String get(Object key) {ByteSequence etcdKey = ByteSequence.from(key.toString(), StandardCharsets.UTF_8);CompletableFuture<GetResponse> future = client.getKVClient().get(etcdKey);try {GetResponse response = future.get();if (response.getCount() > 0) {// 返回获取到的值return response.getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);} else {return null;}} catch (Exception ex) {throw new RuntimeException("Etcd获取值失败", ex);}}// 以下方法为了简化示例,只提供空实现或默认实现// 在实际项目中,需要根据业务需求实现这些方法@Overridepublic void set(String key, String value, long timeout, TimeUnit unit) {// Etcd原生不支持带过期时间的设置,可以通过租约机制实现// 这里为了简化,直接调用普通set方法set(key, value);}@Overridepublic Boolean setIfAbsent(String key, String value) {// 分布式锁或原子操作,可以使用Etcd的事务机制实现return null;}// 其他方法省略空实现...@Overridepublic RedisOperations<String, String> getOperations() {return null;}
}
6. 空适配器
package com.YA33.desgin.adapter.spring.etcd;import org.springframework.data.redis.core.RedisKeyValueAdapter;/*** 空Redis键值适配器* 用于满足Spring Data Redis的自动配置要求*/
public class VoidRedisKeyValueAdapter extends RedisKeyValueAdapter {@Overridepublic void afterPropertiesSet() {// 空实现,避免父类的初始化逻辑}
}
7. 应用启动类
package com.YA33.desgin.adapter;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** 应用启动类*/
@SpringBootApplication
public class AdapterApplication {public static void main(String[] args) {SpringApplication.run(AdapterApplication.class, args);}
}

配置文件

application.yml中配置Etcd连接:

ya33:etcd:url: http://localhost:2379spring:data:redis:# 禁用Spring Boot自带的Redis自动配置repositories:enabled: false

日志系统适配器示例

在实际项目中,我们经常需要集成不同的日志框架。下面通过一个日志适配器的例子,展示如何统一不同日志框架的接口。

场景描述

假设我们有一个老系统使用java.util.logging,但新系统使用Log4j2。我们希望在不修改老系统代码的情况下,将日志输出统一到Log4j2

代码实现

1. 目标接口(统一的日志接口)
package com.YA33.desgin.adapter.logging;/*** 统一的日志接口* 这是我们希望所有系统都使用的接口*/
public interface UnifiedLogger {/*** 记录调试级别日志* @param message 日志消息*/void debug(String message);/*** 记录信息级别日志* @param message 日志消息*/void info(String message);/*** 记录错误级别日志* @param message 日志消息* @param throwable 异常信息*/void error(String message, Throwable throwable);/*** 记录警告级别日志* @param message 日志消息*/void warn(String message);
}
2. 被适配者(Java Util Logging)
package com.YA33.desgin.adapter.logging;import java.util.logging.Level;
import java.util.logging.Logger;/*** Java Util Logging 实现* 这是老系统使用的日志框架*/
public class JavaUtilLogger {private final Logger logger;public JavaUtilLogger(String name) {this.logger = Logger.getLogger(name);}/*** Java Util Logging 的日志方法* 方法签名与我们的统一接口不同*/public void log(Level level, String message) {logger.log(level, message);}public void log(Level level, String message, Throwable thrown) {logger.log(level, message, thrown);}/*** 其他Java Util Logging特有的方法*/public void fine(String message) {logger.fine(message);}public void severe(String message) {logger.severe(message);}
}
3. 适配器实现
package com.YA33.desgin.adapter.logging;import java.util.logging.Level;/*** Java Util Logging 到统一日志接口的适配器* 让老系统的日志框架能够适配新的统一接口*/
public class JavaUtilLoggingAdapter implements UnifiedLogger {private final JavaUtilLogger julLogger;/*** 构造函数,传入需要适配的Java Util Logger* @param loggerName 日志器名称*/public JavaUtilLoggingAdapter(String loggerName) {this.julLogger = new JavaUtilLogger(loggerName);}/*** 构造函数,传入现有的Java Util Logger实例* @param julLogger Java Util Logger实例*/public JavaUtilLoggingAdapter(JavaUtilLogger julLogger) {this.julLogger = julLogger;}@Overridepublic void debug(String message) {// 将debug级别映射到Java Util Logging的FINE级别julLogger.log(Level.FINE, message);}@Overridepublic void info(String message) {// 将info级别映射到Java Util Logging的INFO级别julLogger.log(Level.INFO, message);}@Overridepublic void error(String message, Throwable throwable) {// 将error级别映射到Java Util Logging的SEVERE级别julLogger.log(Level.SEVERE, message, throwable);}@Overridepublic void warn(String message) {// 将warn级别映射到Java Util Logging的WARNING级别julLogger.log(Level.WARNING, message);}
}
4. Log4j2 适配器
package com.YA33.desgin.adapter.logging;import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Logger;/*** Log4j2 到统一日志接口的适配器* 让Log4j2框架能够适配我们的统一接口*/
public class Log4j2Adapter implements UnifiedLogger {private final Logger log4jLogger;public Log4j2Adapter(String loggerName) {this.log4jLogger = (Logger) LogManager.getLogger(loggerName);}public Log4j2Adapter(Class<?> clazz) {this.log4jLogger = (Logger) LogManager.getLogger(clazz);}@Overridepublic void debug(String message) {log4jLogger.debug(message);}@Overridepublic void info(String message) {log4jLogger.info(message);}@Overridepublic void error(String message, Throwable throwable) {log4jLogger.error(message, throwable);}@Overridepublic void warn(String message) {log4jLogger.warn(message);}
}
5. 日志工厂
package com.YA33.desgin.adapter.logging;/*** 日志工厂类* 根据配置创建不同类型的日志适配器*/
public class LoggerFactory {/*** 日志类型枚举*/public enum LoggerType {JAVA_UTIL_LOGGING,LOG4J2}private static LoggerType currentType = LoggerType.LOG4J2;/*** 设置当前使用的日志类型* @param type 日志类型*/public static void setLoggerType(LoggerType type) {currentType = type;}/*** 获取日志器* @param clazz 类* @return 统一日志接口实例*/public static UnifiedLogger getLogger(Class<?> clazz) {return getLogger(clazz.getName());}/*** 获取日志器* @param name 日志器名称* @return 统一日志接口实例*/public static UnifiedLogger getLogger(String name) {switch (currentType) {case JAVA_UTIL_LOGGING:return new JavaUtilLoggingAdapter(name);case LOG4J2:return new Log4j2Adapter(name);default:return new Log4j2Adapter(name);}}
}
6. 使用示例
package com.YA33.desgin.adapter.logging;/*** 日志适配器使用示例*/
public class LoggingExample {// 使用统一日志接口,不关心底层实现private static final UnifiedLogger logger = LoggerFactory.getLogger(LoggingExample.class);public void processData() {logger.debug("开始处理数据");try {// 模拟业务逻辑logger.info("数据处理中...");// 模拟异常情况if (Math.random() > 0.8) {throw new RuntimeException("模拟的业务异常");}logger.info("数据处理完成");} catch (Exception e) {logger.error("数据处理失败", e);}}public static void main(String[] args) {LoggingExample example = new LoggingExample();// 使用Log4j2作为日志后端LoggerFactory.setLoggerType(LoggerFactory.LoggerType.LOG4J2);System.out.println("使用Log4j2后端:");example.processData();// 切换到Java Util LoggingLoggerFactory.setLoggerType(LoggerFactory.LoggerType.JAVA_UTIL_LOGGING);System.out.println("\n使用Java Util Logging后端:");example.processData();}
}

支付系统适配器示例

另一个常见的适配器模式应用场景是支付系统集成。不同的支付渠道(支付宝、微信支付、银联等)有不同的接口,我们可以使用适配器模式统一这些接口。

代码实现

1. 统一支付接口
package com.YA33.desgin.adapter.payment;import java.math.BigDecimal;/*** 统一支付接口* 定义所有支付渠道都需要实现的方法*/
public interface PaymentGateway {/*** 支付方法* @param orderId 订单ID* @param amount 支付金额* @param extraParams 额外参数* @return 支付结果*/PaymentResult pay(String orderId, BigDecimal amount, String extraParams);/*** 查询支付状态* @param orderId 订单ID* @return 支付状态*/PaymentStatus queryStatus(String orderId);/*** 退款方法* @param orderId 订单ID* @param amount 退款金额* @return 退款结果*/RefundResult refund(String orderId, BigDecimal amount);/*** 获取支付渠道名称* @return 渠道名称*/String getChannelName();
}/*** 支付结果*/
class PaymentResult {private boolean success;private String message;private String transactionId;private String payUrl; // 用于扫码支付// 构造函数、getter、setter省略
}/*** 支付状态*/
class PaymentStatus {private String orderId;private String status; // SUCCESS, FAILED, PROCESSINGprivate BigDecimal amount;// 构造函数、getter、setter省略
}/*** 退款结果*/
class RefundResult {private boolean success;private String message;private String refundId;// 构造函数、getter、setter省略
}
2. 支付宝支付适配器
package com.YA33.desgin.adapter.payment;import java.math.BigDecimal;/*** 支付宝支付适配器* 将支付宝的接口适配到统一的支付接口*/
public class AlipayAdapter implements PaymentGateway {// 模拟支付宝SDKprivate final AlipayService alipayService;public AlipayAdapter(String appId, String privateKey) {this.alipayService = new AlipayService(appId, privateKey);}@Overridepublic PaymentResult pay(String orderId, BigDecimal amount, String extraParams) {// 调用支付宝原生接口AlipayResponse response = alipayService.createPayment(orderId, amount.doubleValue(), extraParams);// 将支付宝响应适配为统一支付结果PaymentResult result = new PaymentResult();result.setSuccess("SUCCESS".equals(response.getCode()));result.setMessage(response.getMsg());result.setTransactionId(response.getTradeNo());result.setPayUrl(response.getPayUrl());return result;}@Overridepublic PaymentStatus queryStatus(String orderId) {AlipayQueryResponse response = alipayService.queryOrder(orderId);PaymentStatus status = new PaymentStatus();status.setOrderId(orderId);// 将支付宝状态映射为统一状态switch (response.getTradeStatus()) {case "TRADE_SUCCESS":status.setStatus("SUCCESS");break;case "TRADE_CLOSED":status.setStatus("FAILED");break;default:status.setStatus("PROCESSING");}status.setAmount(BigDecimal.valueOf(response.getTotalAmount()));return status;}@Overridepublic RefundResult refund(String orderId, BigDecimal amount) {AlipayRefundResponse response = alipayService.refund(orderId, amount.doubleValue());RefundResult result = new RefundResult();result.setSuccess("10000".equals(response.getCode()));result.setMessage(response.getMsg());result.setRefundId(response.getRefundId());return result;}@Overridepublic String getChannelName() {return "支付宝";}
}/*** 模拟支付宝服务类*/
class AlipayService {private String appId;private String privateKey;public AlipayService(String appId, String privateKey) {this.appId = appId;this.privateKey = privateKey;}public AlipayResponse createPayment(String orderId, double amount, String subject) {// 模拟调用支付宝接口System.out.println("调用支付宝支付接口: " + orderId + ", 金额: " + amount);AlipayResponse response = new AlipayResponse();response.setCode("SUCCESS");response.setMsg("成功");response.setTradeNo("ALIPAY_" + System.currentTimeMillis());response.setPayUrl("https://alipay.com/pay/" + response.getTradeNo());return response;}public AlipayQueryResponse queryOrder(String orderId) {// 模拟查询订单状态AlipayQueryResponse response = new AlipayQueryResponse();response.setTradeStatus("TRADE_SUCCESS");response.setTotalAmount(100.0);return response;}public AlipayRefundResponse refund(String orderId, double amount) {// 模拟退款AlipayRefundResponse response = new AlipayRefundResponse();response.setCode("10000");response.setMsg("成功");response.setRefundId("REFUND_" + System.currentTimeMillis());return response;}
}/*** 模拟支付宝响应类*/
class AlipayResponse {private String code;private String msg;private String tradeNo;private String payUrl;// getter、setter省略
}class AlipayQueryResponse {private String tradeStatus;private double totalAmount;// getter、setter省略
}class AlipayRefundResponse {private String code;private String msg;private String refundId;// getter、setter省略
}
3. 微信支付适配器
package com.YA33.desgin.adapter.payment;import java.math.BigDecimal;/*** 微信支付适配器* 将微信支付的接口适配到统一的支付接口*/
public class WechatPayAdapter implements PaymentGateway {private final WechatPayService wechatPayService;public WechatPayAdapter(String appId, String mchId, String apiKey) {this.wechatPayService = new WechatPayService(appId, mchId, apiKey);}@Overridepublic PaymentResult pay(String orderId, BigDecimal amount, String extraParams) {// 微信支付需要将金额转换为分int totalFee = amount.multiply(BigDecimal.valueOf(100)).intValue();WechatPayResponse response = wechatPayService.unifiedOrder(orderId, totalFee, extraParams);PaymentResult result = new PaymentResult();result.setSuccess("SUCCESS".equals(response.getReturnCode()) && "SUCCESS".equals(response.getResultCode()));result.setMessage(response.getReturnMsg());result.setTransactionId(response.getTransactionId());result.setPayUrl(response.getCodeUrl()); // 微信支付返回的是二维码URLreturn result;}@Overridepublic PaymentStatus queryStatus(String orderId) {WechatQueryResponse response = wechatPayService.orderQuery(orderId);PaymentStatus status = new PaymentStatus();status.setOrderId(orderId);// 将微信支付状态映射为统一状态switch (response.getTradeState()) {case "SUCCESS":status.setStatus("SUCCESS");break;case "REFUND":status.setStatus("REFUNDED");break;default:status.setStatus("PROCESSING");}// 微信支付返回的是分,需要转换为元status.setAmount(BigDecimal.valueOf(response.getTotalFee() / 100.0));return status;}@Overridepublic RefundResult refund(String orderId, BigDecimal amount) {int refundFee = amount.multiply(BigDecimal.valueOf(100)).intValue();WechatRefundResponse response = wechatPayService.refund(orderId, refundFee);RefundResult result = new RefundResult();result.setSuccess("SUCCESS".equals(response.getReturnCode()) && "SUCCESS".equals(response.getResultCode()));result.setMessage(response.getReturnMsg());result.setRefundId(response.getRefundId());return result;}@Overridepublic String getChannelName() {return "微信支付";}
}/*** 模拟微信支付服务类*/
class WechatPayService {private String appId;private String mchId;private String apiKey;public WechatPayService(String appId, String mchId, String apiKey) {this.appId = appId;this.mchId = mchId;this.apiKey = apiKey;}public WechatPayResponse unifiedOrder(String orderId, int totalFee, String body) {// 模拟调用微信支付接口System.out.println("调用微信支付统一下单接口: " + orderId + ", 金额(分): " + totalFee);WechatPayResponse response = new WechatPayResponse();response.setReturnCode("SUCCESS");response.setReturnMsg("OK");response.setResultCode("SUCCESS");response.setTransactionId("WECHAT_" + System.currentTimeMillis());response.setCodeUrl("weixin://wxpay/bizpayurl?pr=" + System.currentTimeMillis());return response;}public WechatQueryResponse orderQuery(String orderId) {// 模拟查询订单WechatQueryResponse response = new WechatQueryResponse();response.setTradeState("SUCCESS");response.setTotalFee(10000); // 100元return response;}public WechatRefundResponse refund(String orderId, int refundFee) {// 模拟退款WechatRefundResponse response = new WechatRefundResponse();response.setReturnCode("SUCCESS");response.setReturnMsg("OK");response.setResultCode("SUCCESS");response.setRefundId("WECHAT_REFUND_" + System.currentTimeMillis());return response;}
}/*** 模拟微信支付响应类*/
class WechatPayResponse {private String returnCode;private String returnMsg;private String resultCode;private String transactionId;private String codeUrl;// getter、setter省略
}class WechatQueryResponse {private String tradeState;private int totalFee;// getter、setter省略
}class WechatRefundResponse {private String returnCode;private String returnMsg;private String resultCode;private String refundId;// getter、setter省略
}
4. 支付服务工厂
package com.YA33.desgin.adapter.payment;import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;/*** 支付服务工厂* 根据支付渠道创建对应的支付适配器*/
public class PaymentServiceFactory {private static final Map<String, PaymentGateway> gateways = new HashMap<>();static {// 初始化支付渠道gateways.put("alipay", new AlipayAdapter("2021000116691234", "your_private_key"));gateways.put("wechat", new WechatPayAdapter("wx1234567890", "1230000109", "your_api_key"));}/*** 获取支付网关* @param channel 支付渠道* @return 支付网关实例*/public static PaymentGateway getPaymentGateway(String channel) {PaymentGateway gateway = gateways.get(channel);if (gateway == null) {throw new IllegalArgumentException("不支持的支付渠道: " + channel);}return gateway;}/*** 注册新的支付渠道* @param channel 渠道标识* @param gateway 支付网关*/public static void registerGateway(String channel, PaymentGateway gateway) {gateways.put(channel, gateway);}
}
5. 统一支付服务
package com.YA33.desgin.adapter.payment;import java.math.BigDecimal;/*** 统一支付服务* 业务层使用这个服务,不关心具体的支付渠道实现*/
public class UnifiedPaymentService {/*** 创建支付* @param orderId 订单ID* @param amount 金额* @param channel 支付渠道* @param extraParams 额外参数* @return 支付结果*/public PaymentResult createPayment(String orderId, BigDecimal amount, String channel, String extraParams) {PaymentGateway gateway = PaymentServiceFactory.getPaymentGateway(channel);System.out.println("使用支付渠道: " + gateway.getChannelName());return gateway.pay(orderId, amount, extraParams);}/*** 查询支付状态* @param orderId 订单ID* @param channel 支付渠道* @return 支付状态*/public PaymentStatus queryPaymentStatus(String orderId, String channel) {PaymentGateway gateway = PaymentServiceFactory.getPaymentGateway(channel);return gateway.queryStatus(orderId);}/*** 执行退款* @param orderId 订单ID* @param amount 退款金额* @param channel 支付渠道* @return 退款结果*/public RefundResult refund(String orderId, BigDecimal amount, String channel) {PaymentGateway gateway = PaymentServiceFactory.getPaymentGateway(channel);return gateway.refund(orderId, amount);}
}
6. 使用示例
package com.YA33.desgin.adapter.payment;import java.math.BigDecimal;/*** 支付适配器使用示例*/
public class PaymentExample {public static void main(String[] args) {UnifiedPaymentService paymentService = new UnifiedPaymentService();String orderId = "ORDER_" + System.currentTimeMillis();BigDecimal amount = new BigDecimal("100.00");// 使用支付宝支付System.out.println("=== 支付宝支付 ===");PaymentResult alipayResult = paymentService.createPayment(orderId, amount, "alipay", "购买商品");System.out.println("支付结果: " + alipayResult.isSuccess());System.out.println("支付URL: " + alipayResult.getPayUrl());// 使用微信支付System.out.println("\n=== 微信支付 ===");PaymentResult wechatResult = paymentService.createPayment(orderId, amount, "wechat", "购买商品");System.out.println("支付结果: " + wechatResult.isSuccess());System.out.println("支付URL: " + wechatResult.getPayUrl());// 查询支付状态System.out.println("\n=== 查询支付状态 ===");PaymentStatus status = paymentService.queryPaymentStatus(orderId, "alipay");System.out.println("订单状态: " + status.getStatus());// 执行退款System.out.println("\n=== 执行退款 ===");RefundResult refundResult = paymentService.refund(orderId, amount, "alipay");System.out.println("退款结果: " + refundResult.isSuccess());}
}

适配器模式的关键要点总结

通过以上两个完整的示例,我们可以总结出适配器模式的关键要点:

1. 适配器模式的结构

客户端 → 目标接口 → 适配器 → 被适配者

2. 实现步骤

  1. 定义目标接口:确定客户端期望的接口
  2. 识别被适配者:找出需要被适配的现有类或接口
  3. 创建适配器类
    • 实现目标接口
    • 持有被适配者的引用
    • 在目标接口方法中调用被适配者的方法

3. 适配器模式的变体

  • 类适配器:使用继承(在Java中不常用,因为Java不支持多继承)
  • 对象适配器:使用组合(推荐使用,更灵活)

4. 适用场景

  • 系统集成:集成第三方库或遗留系统
  • 接口统一:统一多个相似但不兼容的接口
  • 版本兼容:新版本接口需要兼容老版本
  • 测试模拟:在测试中使用模拟对象替代真实对象

5. 优点

  • 解耦合:客户端与被适配者解耦
  • 复用性:可以复用现有的类
  • 灵活性:可以适配多个不同的被适配者
  • 符合开闭原则:易于扩展新的适配器

6. 注意事项

  • 不要过度使用:如果接口可以统一修改,直接修改接口可能更简单
  • 性能考虑:适配器会增加额外的调用层次,可能影响性能
  • 复杂性:过多的适配器会增加系统的复杂性
http://www.dtcms.com/a/418758.html

相关文章:

  • 语音识别的评价指标
  • 成都建设企业网站果麦传媒的网站怎么做的
  • python:Django 和 Vue.js 技术栈解析
  • (二十六)、Kuboard 部署网络问题 k8s 使用本地镜像 k8s使用 register本地镜像站 综合应用
  • 腾讯云上TKE集群中通过clb-ingress公网访问到后端服务
  • 信阳做网站公司编程培训机构加盟怎样
  • vps空间如何做网站备份如何提高网站的搜索
  • 广州免费自助建站开发建设工程什么意思
  • Apache Doris 4.0 AI 能力揭秘(二):为企业级应用而生的 AI 函数设计与实践
  • 用deepseek部署全自动的机器人--bytebot
  • 网站开发者模式下怎么保存图片建设网站空间
  • 兰州新区建站07073游戏网
  • 营销型网站建站教程wordpress edit lock
  • 如何将华为手机的照片转移到电脑
  • 云手机的主要核心是什么
  • 三防手机是什么?有哪些值得购入的三防手机?
  • 网站的文件夹上海网站备案在哪里查询
  • 秋招算法记录 | 排序算法整理 | 直接选择、直接插入、冒泡、快排、希尔排序
  • Docker 容器如何实现隔离
  • ThinkPHP8学习篇(八):数据库(四)
  • 专业商城网站设计制作wordpress用户中心制作
  • 加强住房公积金网站建设搜索推广的优势
  • 【BTC】比特币脚本
  • 人工智能吉他教学研究:基于Liberlive与TemPolor的产品与教学思考[特殊字符]
  • 电脑开机显示屏显示无信号怎么办 原因及解决方法
  • 怎么攻击织梦网站网站发布 图片看不到
  • ROS1 go2 vlp16 局部避障--3 篇
  • 【奇怪的bug】lua的nil不报错
  • 有哪些做短租的网站好浙江省建设厅网站查询
  • 南宁网站建设优势吉林省软环境建设办公室网站