偶然看到一篇文章, https://www.cnblogs.com/buguge/p/19055703
对这篇文章的设计进行了更改。
原来设计的类图 和流程图 :


原有设计的优缺点:
优点
实现了解耦
-
核心优势:业务层(插件)不依赖于上层应用的具体实现,而是依赖于一个抽象的接口 (TransferCallback)。这符合依赖倒置原则 (DIP),是插件化架构的基石。
-
灵活性:上层应用可以自由决定是否关心回调事件(通过选择是否实现接口并注入),以及如何响应事件(在 onTransferSuccess 方法中实现任何业务逻辑)。
对应用层封装了异步复杂性
异步线程的创建、管理和任务提交完全由业务层(AccountTransferService)内部完成。上层应用只需调用一个同步风格的 accounting() 方法,无需关心其内部是多线程实现的,简化了调用方的使用。
“一站式”服务
对于上层应用来说,它只需要与 AccountTransferService 打交道。它既通过这个服务执行了转账操作,也通过它获得了操作完成的通知。如果不需要通知,甚至不需要感知 TransferCallback 接口的存在。这在简单场景下显得非常简洁。
缺点
破坏了单一职责原则 (SRP)
- 核心问题:AccountTransferService 承担了过多的职责。它不仅是“转账业务”的执行者,还是“异步任务”的管理者,同时又是“事件”的发布者。这导致其职责不纯粹、不单一,变得臃肿,违反了高内聚的设计目标。
隐藏的控制流和调试困难
-
回调函数使得程序的执行流程变得不直观。当阅读 AccountTransferService 的代码时,无法直接看出 transferCallback.onTransferSuccess() 最终会执行哪段代码,必须去查找被注入的具体实现是什么。
-
当回调函数中出现 bug 时,栈追踪信息会分散在两个不同的线程和模块中,增加了定位问题的难度。
有限的灵活性和可复用性
-
强绑定通知机制:业务层强制规定了通知的机制(同步回调)。如果上层应用希望用事件总线、消息队列等其他方式来处理通知,将无法实现,除非修改业务层代码。
-
无法选择同步执行:业务层硬编码了异步逻辑。如果某个场景下希望同步等待转账完全完成后再继续,这个设计无法满足。
-
复用性差:如果想在另一个不需要通知的项目中复用 AccountTransferService,会发现它依然拖着异步线程池和回调检查的逻辑,无法作为一个纯净的业务组件使用。
生命周期和并发管理复杂
-
业务层需要自己管理线程池 (ExecutorService) 的生命周期(创建、关闭),这增加了其复杂度。
-
在回调应用中,需要考虑线程安全问题。如果多个线程可能同时修改回调函数引用(通过 setter),需要做同步处理。
错误处理机制不完善
- 原始设计只考虑了成功回调 (onTransferSuccess),没有定义失败回调。一个健壮的系统必须考虑异步操作可能失败的情况,而原有设计在这方面是缺失的。
新的设计:


业务层(插件层)- 纯粹的同步逻辑
// 转账结果类
public class TransferResult {
private final String transferOrderNo;
private final boolean success;
private final String message;
public TransferResult(String transferOrderNo, boolean success, String message) {
this.transferOrderNo = transferOrderNo;
this.success = success;
this.message = message;
}
// Getters
public String getTransferOrderNo() { return transferOrderNo; }
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
@Override
public String toString() {
return "TransferResult{" +
"transferOrderNo='" + transferOrderNo + '\'' +
", success=" + success +
", message='" + message + '\'' +
'}';
}
}
// 账户转账服务 - 纯粹的业务逻辑,完全同步
public class AccountTransferService {
private final AccountingService accountingService = new AccountingService();
// 同步执行转账,不包含任何异步或通知逻辑
public TransferResult accountingSync(AccountTransfer accountTransfer) {
System.out.println("转账记账开始 - 同步执行");
try {
// 同步处理转出账户
AccountingRequest fromRequest = new AccountingRequest(
accountTransfer.getTransferOrderNo(),
accountTransfer.getFrom(),
accountTransfer.getTransferAmount(),
true
);
accountingService.accounting(fromRequest);
// 同步处理收款方
Map<String, Double> tos = accountTransfer.getTos();
for (Map.Entry<String, Double> entry : tos.entrySet()) {
AccountingRequest toRequest = new AccountingRequest(
accountTransfer.getTransferOrderNo(),
entry.getKey(),
entry.getValue(),
false
);
accountingService.accounting(toRequest);
}
return new TransferResult(
accountTransfer.getTransferOrderNo(),
true,
"转账成功完成"
);
} catch (Exception e) {
return new TransferResult(
accountTransfer.getTransferOrderNo(),
false,
"转账失败: " + e.getMessage()
);
}
}
}
// 记账服务
public class AccountingService {
public void accounting(AccountingRequest request) {
System.out.println("AccountingService->记账完成. " +
"业务单号=" + request.getOrderNo() +
", accountNo=" + request.getAccountNo() +
", amount=" + request.getAmount() +
(request.isFrom() ? " (转出)" : " (转入)"));
}
}
// 账户转账请求
public class AccountTransfer {
private String transferOrderNo;
private String from;
private double transferAmount;
private Map<String, Double> tos;
// Getters and setters
public String getTransferOrderNo() { return transferOrderNo; }
public AccountTransfer setTransferOrderNo(String transferOrderNo) {
this.transferOrderNo = transferOrderNo;
return this;
}
public String getFrom() { return from; }
public AccountTransfer setFrom(String from) {
this.from = from;
return this;
}
public double getTransferAmount() { return transferAmount; }
public AccountTransfer setTransferAmount(double transferAmount) {
this.transferAmount = transferAmount;
return this;
}
public Map<String, Double> getTos() { return tos; }
public AccountTransfer setTos(Map<String, Double> tos) {
this.tos = tos;
return this;
}
}
// 记账请求
public class AccountingRequest {
private final String orderNo;
private final String accountNo;
private final double amount;
private final boolean isFrom;
public AccountingRequest(String orderNo, String accountNo, double amount, boolean isFrom) {
this.orderNo = orderNo;
this.accountNo = accountNo;
this.amount = amount;
this.isFrom = isFrom;
}
// Getters
public String getOrderNo() { return orderNo; }
public String getAccountNo() { return accountNo; }
public double getAmount() { return amount; }
public boolean isFrom() { return isFrom; }
}
- 应用层 - 包含异步和通知逻辑
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 应用层服务 - 包含异步和通知逻辑
public class TransferOrderService {
private final AccountTransferService accountTransferService;
private final ExecutorService executorService;
public TransferOrderService() {
this.accountTransferService = new AccountTransferService();
this.executorService = Executors.newFixedThreadPool(3);
}
// 应用层决定如何使用业务层服务(同步或异步)
public void transfer() {
System.out.println("转账单记账 - 应用层");
// 准备转账数据
AccountTransfer accountTransfer = new AccountTransfer();
accountTransfer.setTransferOrderNo("T202508000001")
.setFrom("A")
.setTransferAmount(50.00);
accountTransfer.setTos(Map.of("B", 10.00, "C", 40.00));
// 应用层决定使用异步方式调用业务层服务
executorService.submit(() -> {
try {
// 调用业务层的同步方法
TransferResult result = accountTransferService.accountingSync(accountTransfer);
// 应用层处理结果和通知
if (result.isSuccess()) {
onTransferSuccess(result);
} else {
onTransferFailure(result);
}
} catch (Exception e) {
System.err.println("转账过程中发生异常: " + e.getMessage());
onTransferFailure(new TransferResult(
accountTransfer.getTransferOrderNo(),
false,
"转账异常: " + e.getMessage()
));
}
});
}
// 应用层处理成功通知
public void onTransferSuccess(TransferResult result) {
System.out.println("=========当前是在应用层,进行转账完成后的业务处理");
System.out.println("=========已向收款人发送到账通知短消息");
System.out.println("=========转账结果: " + result);
// 这里可以添加更多的应用层业务逻辑
// 比如:更新订单状态、发送消息、记录日志等
}
// 应用层处理失败通知
public void onTransferFailure(TransferResult result) {
System.out.println("=========转账失败,执行补偿操作");
System.out.println("=========失败原因: " + result.getMessage());
// 这里可以添加失败处理逻辑
// 比如:回滚操作、发送告警等
}
// 关闭线程池
public void shutdown() {
executorService.shutdown();
}
}
- 演示主程序
public class ApplicationDemo {
public static void main(String[] args) {
// 创建应用层服务
TransferOrderService transferOrderService = new TransferOrderService();
// 执行转账
transferOrderService.transfer();
// 等待异步操作完成
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 清理资源
transferOrderService.shutdown();
}
}
新版本设计(职责完全分离)的优势与劣势分析
优势 ✅
1. 极致的职责分离与单一职责原则
- 业务层纯粹性:
AccountTransferService只关注核心转账逻辑,不包含任何异步、通知或线程管理代码 - 应用层专注性:
TransferOrderService专门处理业务协调、异步调度和通知逻辑 - 符合高内聚低耦合:每个模块都有明确且单一的职责,模块间通过清晰接口通信
2. 卓越的可测试性
- 业务层测试简单:可以轻松测试同步转账逻辑,无需模拟异步环境或回调机制
- 独立测试:业务层和应用层可以完全独立测试,互不干扰
- 测试覆盖率提高:清晰的职责边界使得编写单元测试更加直观和全面
3. 高度可复用性
- 业务层通用性:纯净的同步业务逻辑可以被任何需要转账功能的应用复用
- 多种使用方式:应用层可以根据需要选择同步或异步调用业务层
- 框架无关性:业务层不依赖任何特定框架或模式,迁移成本低
4. 更清晰的架构与可维护性
- 直观的代码流程:执行路径明确,没有隐藏的回调或间接调用
- 易于理解:新开发者能够快速理解系统结构和数据流
- 长期维护友好:职责清晰减少代码腐化,便于后续扩展和修改
5. 灵活性与扩展性
- 通知机制灵活:应用层可以自由选择通知方式(日志、事件、消息队列等)
- 异步策略可定制:可以轻松实现不同的异步模式(如重试、批处理、熔断等)
- 易于集成新功能:新增功能只需在适当层级添加,不会影响其他部分
劣势 ❌
1. 应用层复杂度增加
- 代码冗余风险:每个使用业务层的地方都需要重复实现类似的异步和通知逻辑
- 开发工作量增加:需要应用层开发者处理更多基础架构 concerns
- 学习曲线稍陡:开发者需要理解分层架构和职责分离的理念
2. 业务层无法主动通知
- 被动服务模式:业务层只是功能提供者,无法在特定事件发生时主动通知应用层
- 实时性受限:对于需要业务层主动推送信息的场景,此模式不适用
3. 可能存在性能开销
- 数据传递开销:需要通过返回值传递结果,而不是直接回调,可能增加少量数据复制开销
- 上下文切换:应用层管理异步可能增加线程上下文切换(但通常可忽略)
4. 初期开发效率可能略低
- 设计决策增多:需要更多前期设计思考,确定哪些逻辑属于业务层,哪些属于应用层
- 模板代码增加:需要编写更多的"胶水代码"来连接各层
适用场景推荐
适合采用新设计的场景
- ✅ 大型复杂企业应用系统
- ✅ 需要高度可测试性和可维护性的项目
- ✅ 业务逻辑需要被多个应用复用的场景
- ✅ 长期演进和持续维护的项目
- ✅ 团队注重代码质量和架构整洁度
可能不适合的场景
- ❌ 简单的一次性脚本或原型开发
- ❌ 极度追求开发速度的初创项目MVP阶段
- ❌ 业务层需要主动推送信息的实时系统
- ❌ 团队对分层架构理解不足且时间紧迫
总结
新版本设计通过极致的职责分离,牺牲了一定的开发便利性,换来了卓越的可维护性、可测试性和灵活性。这种权衡在大多数企业级应用开发中是值得的,特别是对于需要长期维护和演进的系统。
| 特性 | 新版本设计 | 旧版本设计 |
|---|---|---|
| 职责清晰度 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| 可测试性 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| 可复用性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 开发效率 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 长期维护 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| 灵活性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
推荐使用:对于大多数严肃的商业项目,推荐采用新版本设计,因为它为系统的长期健康奠定了更好的架构基础。
浙公网安备 33010602011771号