本文目录导读:

Java项目重构是一个非常经典且实用的话题,一个好的重构案例不仅能展示代码优化技巧,更能体现架构思维的演进。
下面我将从一个典型的“银行转账”业务入手,展示一个从面向过程 + 贫血模型逐步重构为领域驱动 + 设计模式的完整案例。
背景:一个“腐化”的转账服务
假设我们接手了一个老项目,核心功能是转账,最初的代码可能长这样:
// 这是最初版本的代码,常见于CRUD为主的系统
@Service
@Transactional
public class TransferServiceV1 {
@Autowired
private AccountMapper accountMapper; // MyBatis Mapper
@Autowired
private LedgerMapper ledgerMapper;
public String transfer(Long fromId, Long toId, BigDecimal amount) {
// 1. 校验
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
return "金额不合法";
}
Account from = accountMapper.selectById(fromId);
Account to = accountMapper.selectById(toId);
if (from == null || to == null) {
return "账户不存在";
}
if (from.getBalance().compareTo(amount) < 0) {
return "余额不足";
}
// 2. 业务逻辑(挂在Service层)
// 注意:这里的负数表示扣款,正数表示加款
BigDecimal newFromBalance = from.getBalance().subtract(amount);
BigDecimal newToBalance = to.getBalance().add(amount);
// 3. 更新数据库(散落的数据访问)
from.setBalance(newFromBalance);
to.setBalance(newToBalance);
accountMapper.updateById(from);
accountMapper.updateById(to);
// 4. 记账(硬编码)
Ledger ledger = new Ledger();
ledger.setFromId(fromId);
ledger.setToId(toId);
ledger.setAmount(amount);
ledger.setTime(new Date());
ledgerMapper.insert(ledger);
return "SUCCESS";
}
}
问题分析:
- 业务逻辑泄漏:核心业务逻辑(余额计算)放在了Service层,Account对象只是一个“数据袋子”。
- 事务边界模糊:如果更新账户成功但记账失败,事务需要回滚,但这里的代码顺序和异常处理不够健壮。
- 扩展性差:如果增加“手续费”、“汇率换算”或“风控校验”,只能不断往这个方法里堆代码。
- 可测试性差:难以脱离Spring和数据库进行单元测试。
重构目标
我们将分两个阶段进行重构:
- 组件化与异常处理 (提升可维护性)
- 引入领域模型与策略模式 (提升扩展性与架构清晰度)
组件化与异常处理
核心思路:把“查账户”、“更新账户”封装成独立的类,并定义业务异常。
// 1. 定义业务异常
public class InsufficientBalanceException extends RuntimeException {
public InsufficientBalanceException(String message) { super(message); }
}
// 2. 将账户操作封装为AccountService(消除贫血模型的一部分)
@Service
public class AccountService {
private final AccountRepository accountRepository;
// 使用更通用的 Repository 替代 Mapper
public Account findAccount(Long id) {
return accountRepository.findById(id)
.orElseThrow(() -> new AccountNotFoundException("账户不存在: " + id));
}
// 扣款操作封装在领域服务中
public void debit(Account account, BigDecimal amount) {
account.debit(amount); // 把操作放回 Account 对象!
}
public void credit(Account account, BigDecimal amount) {
account.credit(amount);
accountRepository.save(account);
}
}
// 3. 重构后的转账服务(专注与流程编排)
@Service
@Transactional
public class TransferServiceV2 {
private final AccountService accountService;
private final LedgerService ledgerService;
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 1. 获取账户(通过Service)
Account from = accountService.findAccount(fromId);
Account to = accountService.findAccount(toId);
// 2. 执行扣款与加款(业务规则被移入领域模型)
from.withdraw(amount); // 内部会校验余额
to.deposit(amount);
// 3. 持久化(由Repository统一管理)
accountService.save(from);
accountService.save(to);
// 4. 记账
ledgerService.recordTransaction(fromId, toId, amount, "TRANSFER");
}
}
关键改进:
- 异常处理:定义了
InsufficientBalanceException,不需要返回字符串,由全局异常处理器统一处理。 - 领域模型苏醒:
Account类开始有行为,如withdraw()和deposit()。 - Repository模式:使用
AccountRepository取代 MyBatis Mapper,便于替换实现和测试。
引入设计模式与领域事件
核心思路:解决扩展性问题,现在要求 “跨行转账需要手续费” 或 “大额转账需要审批”,如果继续叠加if-else,代码会失控。
解决方案:使用 策略模式 处理费用,使用 模板方法模式 处理转账流程。
定义转账策略接口
public interface TransferPolicy {
// 计算手续费
BigDecimal calculateFee(Account from, Account to, BigDecimal amount);
// 是否需要额外审批
boolean requiresApproval(TransferRequest request);
}
实现具体策略
// 策略1:一般手续费
@Component
public class StandardTransferPolicy implements TransferPolicy {
@Override
public BigDecimal calculateFee(Account from, Account to, BigDecimal amount) {
return BigDecimal.ZERO; // 同行转账免费
}
@Override
public boolean requiresApproval(TransferRequest request) {
return false;
}
}
// 策略2:跨行转账(注意:这里通过@Qualifier或名称区分)
@Component("interBankPolicy")
public class InterBankTransferPolicy implements TransferPolicy {
private static final BigDecimal FEE_RATE = new BigDecimal("0.01");
@Override
public BigDecimal calculateFee(Account from, Account to, BigDecimal amount) {
// 假设不同的银行通过Account的bankCode区分
if (!from.getBankCode().equals(to.getBankCode())) {
return amount.multiply(FEE_RATE).setScale(2, RoundingMode.HALF_UP);
}
return BigDecimal.ZERO;
}
@Override
public boolean requiresApproval(TransferRequest request) {
// 跨行转账超5万需要审批
return request.getAmount().compareTo(new BigDecimal("50000")) > 0;
}
}
策略工厂(根据上下文选择策略)
@Component
public class TransferPolicyFactory {
private final List<TransferPolicy> strategies;
// Spring 自动注入所有策略实现
public TransferPolicyFactory(List<TransferPolicy> strategies) {
this.strategies = strategies;
}
public TransferPolicy getPolicy(Account from, Account to, BigDecimal amount) {
// 这里简单模拟:如果是跨行,则选用跨行策略,否则用标准策略
// 实际项目中可以用一个 Matcher 列表来判断
return strategies.stream()
.filter(p -> p instanceof InterBankTransferPolicy)
.findFirst()
.orElseGet(() -> strategies.get(0)); // 默认标准策略
}
}
引入领域事件(解耦“记账”和“通知”)
// 定义一个事件
public record TransferCompletedEvent(Long fromId, Long toId, BigDecimal amount) {}
改造V2版本,发布事件:
@Transactional
@Service
public class TransferServiceV3 {
private final AccountService accountService;
private final TransferPolicyFactory policyFactory;
private final ApplicationEventPublisher eventPublisher;
public void transfer(TransferRequest request) {
Long fromId = request.getFromId();
Long toId = request.getToId();
BigDecimal amount = request.getAmount();
// 1. 获取账户
Account from = accountService.findAccount(fromId);
Account to = accountService.findAccount(toId);
// 2. 策略选择
TransferPolicy policy = policyFactory.getPolicy(from, to, amount);
// 3. 计算手续费
BigDecimal fee = policy.calculateFee(from, to, amount);
// 4. 业务执行(考虑手续费)
from.withdraw(amount.add(fee)); // 扣本金+手续费
to.deposit(amount); // 对方到账本金
accountService.save(from);
accountService.save(to);
// 5. 回填手续费记录(创建账本条目)
ledgerService.recordTransaction(fromId, toId, amount, fee, "TRANSFER");
// 6. 发布事件(完成异步通知等)
eventPublisher.publishEvent(new TransferCompletedEvent(fromId, toId, amount));
}
}
总结对比
| 维度 | V1 (原始) | V2 (组件化) | V3 (领域驱动) |
|---|---|---|---|
| 代码风格 | 面向过程 | 面向对象(部分) | 领域驱动(DDD) |
| 职责划分 | Service完成所有事 | Repository + Service分层 | Service编排 + Domain行为 + Policy策略 |
| 扩展点 | add if-else | 修改原方法 | 新增策略类即可 |
| 测试难度 | 需要MockDB | 需要MockRepository | 可纯单元测试(Mock策略) |
| 业务表达 | 一串数字计算 | withdraw / deposit |
策略匹配 + 领域事件 |
最终建议: 重构不是一蹴而就的。每次重构只做一步,保持测试通过,上述案例展示了如何将“烂代码”逐步演进为干净、可维护、可扩展的系统,在实际项目中,你可以参考这个思路,从封装异常开始,逐步引入设计模式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播