手撕设计模式——支付系统之适配器模式
1.业务需求
大家好,我是菠菜啊,好久不见,今天给大家带来的是——适配器模式。老规矩,在介绍这期内容前,我们先来看看这样的需求:我们现在有一个对接过支付宝方式的支付系统,现在要将支付渠道从支付宝改为微信支付,但是微信支付和支付宝的接口方法名、参数、返回值完全不同,在不改动业务接口的情况下,我们该怎么做呢?
原支付系统代码:
//订单业务层
public class OrderService {
private AliPaySDK aliPaySDK;
public OrderService() {
}
public OrderService(AliPaySDK aliPaySDK) {
this.aliPaySDK = aliPaySDK;
}
// 获取支付链接
public String getPayUrl(String orderId, double orderMoney){
return aliPaySDK.createAlipayPayUrl(orderId,orderMoney);
}
}
//支付宝SDK
public class AliPaySDK {
public String createAlipayPayUrl(String orderId, double orderMoney) {
// 创建支付宝支付链接
return "https://www.alipay.com/pay?orderId=" + orderId + "&orderMoney=" + orderMoney;
}
}
//微信支付SDK
public class WechatPaySDK {
public String createWechatPayUrl(String orderId, BigDecimal orderMoney) {
// 创建支weixin支付链接
return "https://www.wechat.com/pay?orderId=" + orderId + "&orderMoney=" + orderMoney;
}
}
//客户端代码
public class Client {
public static void main(String[] args) {
OrderService orderService = new OrderService(new AliPaySDK());
String payUrl=orderService.createPayUrl("1", 1.0);
System.out.println("生成支付链接:"+payUrl);
}
}
2.代码实现
Talk is cheap,show me your code.
初版实现思路:
我们可以新建一个类继承OrderService类重写createPayUrl接口,封装WechatPaySDK对象,这个其实就是对象适配器模式。
初版代码如下:
//微信支付适配器类
public class WechatPayAdapter extends OrderService {
private WechatPaySDK wechatPaySDK;
public WechatPayAdapter(WechatPaySDK wechatPaySDK) {
super();
this.wechatPaySDK = wechatPaySDK;
}
@Override
public String createPayUrl(String orderId, double orderMoney) {
BigDecimal decimal = BigDecimal.valueOf(orderMoney);
return wechatPaySDK.createWechatPayUrl(orderId,decimal );
}
}
public class Client {
public static void main(String[] args) {
//用微信支付适配器对象替换OrderService对象即可
//OrderService orderService = new OrderService(new AliPaySDK());
OrderService orderService = new WechatPayAdapter(new WechatPaySDK());
String payUrl=orderService.getPayUrl("1", 1.0);
System.out.println("生成支付链接:"+payUrl);
}
}
代码结构图:
思考:
直接用微信支付适配器对象替换订单业务对象,原先OrderService无需修改,每增加一个支付方式只需要新增相应的适配器,满足开闭原则和单一职责原则。这种设计模式叫做适配器模式(对象适配器),用接口转换解决不兼容问题。常见应用的现实场景如国内和国外的电源插头不一致问题,用一个插头转换器即可解决。
3.定义与组成
定义:
适配器模式是将一个类的接口转换成希望的另一个接口,使得原来由于接口不兼容而不能一起工作的那些类一起工作。
组成:
- 目标类(Target):所期望的目标接口,可以是具体或抽象的类,也可以是接口
- 被适配者类(Adaptee):已有的功能类
- 适配器类(Adapter):内部封装了Adaptee对象,把源接口转化为目标接口
4.代码优化
上述代码我们也发现一些问题,可以重构优化一下。
优化代码:
//支付方式适配器接口
public interface PayAdapter {
public String createPayPayUrl(String orderId, double orderMoney);
}
//具体的微信支付适配器实现
public class WechatPayAdapter implements PayAdapter {
private WechatPaySDK wechatPaySDK;
public WechatPayAdapter(WechatPaySDK wechatPaySDK) {
super();
this.wechatPaySDK = wechatPaySDK;
}
@Override
public String createPayPayUrl(String orderId, double orderMoney) {
BigDecimal decimal = BigDecimal.valueOf(orderMoney);
return wechatPaySDK.createWechatPayUrl(orderId,decimal );
}
}
//具体的支付宝支付适配器实现
public class AliPayAdapter implements PayAdapter {
private AliPaySDK aliPaySDK;
public AliPayAdapter(AliPaySDK aliPaySDK) {
super();
this.aliPaySDK = aliPaySDK;
}
@Override
public String createPayPayUrl(String orderId, double orderMoney) {
return aliPaySDK.createAlipayPayUrl(orderId,orderMoney );
}
}
//订单业务服务
public class OrderService {
//private AliPaySDK aliPaySDK;
private PayAdapter payAdapter;
/* public OrderService(AliPaySDK aliPaySDK) {
this.aliPaySDK = aliPaySDK;
}*/
public OrderService(PayAdapter payAdapter) {
this.payAdapter = payAdapter;
}
public OrderService() {
}
// 获取支付链接
public String getPayUrl(String orderId, double orderMoney){
return payAdapter.createPayPayUrl(orderId,orderMoney);
}
}
实现代码结构图:
思考:
上述代码抽象出支付方式适配接口,我们需要主动设计而不是被迫适配兼容,一个是事前设计,一个是事后补救,但也要警惕过度设计。
5.应用示例
适配器模式在Java核心库中有多个经典实现,以下是代表性示例:
5.1 集合框架中的适配器:Arrays.asList()
String[] array = {"Java", "Adapter", "Pattern"}; // 被适配的数组
// 数组适配为List接口
List<String> list = Arrays.asList(array);
// 使用List接口操作
System.out.println(list.size()); // 3
System.out.println(list.contains("Java")); // true
适配关系:
- Target:
List
接口 - Adapter:
Arrays.ArrayList
(内部类) - Adaptee:数组对象
5.2 I/O流中的适配器:InputStreamReader
// 将字节流适配为字符流
FileInputStream fis = new FileInputStream("file.txt"); // 字节流(Adaptee)
// 创建适配器:字节流 → 字符流
Reader reader = new InputStreamReader(fis, "UTF-8");
// 使用字符流接口操作(Target)
BufferedReader br = new BufferedReader(reader);
String line = br.readLine(); // 统一使用字符流接口
适配关系:
- Target:
Reader
抽象类 - Adapter:
InputStreamReader
- Adaptee:
InputStream
及其实现
6.适用场景
6.1.第三方库/服务集成
✅ 场景特征:
- 需要集成不同供应商的SDK(如支付、短信服务)
- 各SDK接口规范不一致
- 无法修改SDK源代码
💡 解决方案:
为每个SDK创建适配器,统一成系统标准接口
6.2 遗留系统现代化改造
✅ 场景特征:
- 老系统使用过时接口(如SOAP服务)
- 新模块需要RESTful接口调用老功能
- 重构旧代码风险高
💡 解决方案:
创建适配器将旧接口转换为RESTful接口
6.3接口版本兼容
✅ 场景特征:
- 新版本API接口发生破坏性变更
- 需要同时支持新旧客户端
- 希望逐步迁移而非强制升级
💡 解决方案:
为旧版接口创建适配器,使其行为匹配新版接口
-
总结:适配器模式的核心价值
适配器是系统集成中的”接口转换器”,它通过:
- 🛡️ 保护现有资产 – 复用无法修改的遗留代码
- 🌉 建立兼容桥梁 – 解决接口不匹配问题
- 🧩 降低耦合度 – 隔离第三方库的依赖
7.知识拓展
7.1 类适配器
还有一种适配器是类适配器,它和对象适配器最大的区别在于对象适配器中适配器类和被适配者类是关联关系,而类适配器和待适配类是继承关系,所以类适配器会有一些限制。被适配者类(Adaptee)如果用final修饰,或者目标类(Target)不是接口,无法使用类适配器。而且合成复用原则中推荐优先使用对象组合方式而不是类继承方式去实现,所以也导致了在Java的面向对象语言中,类适配器很少使用。
//目标接口
public interface Target {
void request();
}
//适配者类
public class Adaptee {
public void specialRequest()
{
System.out.println("specialRequest......");
}
//适配器类
public class Adapter extends Adaptee implements Target {
public void request(){
super.specialRequest();
}
}
7.2 双向适配器
对象适配器类同时拥有适配类和目标接口的引用,并实现这俩个接口,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用被适配者类中的方法,这就是一个双向适配器。
class Adapter implements Target,Adaptee {
//同时有目标接口和被适配者的引用
private Target target;
private Adaptee adaptee;
public Adapter(Target target) {
this.target = target;
}
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
adaptee.specificRequest();
}
public void specificRequest() {
target.request();
}
}
//适配者接口
public interface Adaptee {
void specialRequest();
}
//具体的被适配者实现
public class ConcreateAdaptee implements Adaptee{
@Override
public void specialRequest() {
System.out.println("ConcreateAdaptee-specialRequest......");
}
}
//具体的目标接口实现
public class ConcreateTarget implements Target{
@Override
public void request() {
System.out.println("ConcreateTarget-request......");
}
}
//客户端
public class Client2 {
public static void main(String[] args) {
//双向适配器模式测试
Adaptee adaptee=new ConcreateAdaptee();
Adapter adapter1=new Adapter(adaptee);
System.out.println("正向适配开始++++");
adapter1.request();
System.out.println("正向适配结束++++");
Target target=new ConcreateTarget();
Adapter adapter2=new Adapter(target);
System.out.println("反向适配开始++++");
adapter2.specialRequest();
System.out.println("反向适配开始++++");
}
}
示例代码运行结果:
技术需要沉淀,同样生活也是~
个人链接:博客,欢迎一起交流