# 设计模式 —— 优化退款案例(策略模式 & 模板模式 & 工厂模式)

# 一、背景

在电商 App 的订单模块中,随着业务发展出现了多种订单类型(普通订单、团购订单、组合订单、服务卡订单等),不同订单的退款逻辑差异较大。同时,用户维度(如 VIP 用户、普通用户)也会影响退款流程,例如 VIP 用户可享受垫付退款,普通用户则需等待 2 个工作日。
原有代码因多次迭代新增逻辑,导致大量 if-else 嵌套,代码混乱且难以维护。本次重构通过策略模式模板模式工厂模式整合用户与订单维度的复杂逻辑,实现代码解耦与扩展。

# 二、设计方案

# 1. 策略模式(用户维度)

适用场景

  • 不同用户(VIP / 普通用户)的退款逻辑不同,需动态选择行为。
  • 避免多重条件判断(如 if-else ),将算法封装为独立策略类。

核心思想:定义策略接口,具体策略实现差异化逻辑,通过工厂模式管理策略实例。

# 2. 模板模式(订单维度)

适用场景

  • 不同订单(普通 / 团购订单)的退款流程包含公共步骤(订单校验、更新退款表、计算空跑费),但部分步骤需子类实现。
  • 提取公共逻辑到抽象模板类,子类覆盖差异化步骤。

# 3. 工厂模式

作用:作为容器管理策略模式的实例,通过枚举键值对映射策略类,解耦对象创建逻辑。

# 三、代码实战

# 1. 基础枚举定义

# 用户等级枚举(UserLevelEnum)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public enum UserLevelEnum {
NORMAL(1, "普通用户"),
VIP(2, "VIP用户");

private Integer code;
private String desc;

UserLevelEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}

public Integer getCode() { return code; }
}

# 订单类型枚举(OrderTypeEnum)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public enum OrderTypeEnum {
NORMAL_ORDER(1, "普通订单"),
GROUP_ORDER(2, "团购订单"),
COMBO_ORDER(3, "组合订单");

private Integer code;
private String desc;

OrderTypeEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}

public Integer getCode() { return code; }
}

# 2. 策略模式实现(用户退款策略)

# 策略接口(IRefundUserStrategy)

1
2
3
public interface IRefundUserStrategy {
void doRefundByUser(OrderInfo refundVO); // 退款主逻辑
}

# 普通用户策略(NormalUserRefundStrategy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class NormalUserRefundStrategy implements IRefundUserStrategy {

@Autowired
private OrderRefundComponent orderRefundComponent;

@PostConstruct // Spring初始化后注册策略
private void initStrategy() {
RefundUserStrategyFactory.putStrategy(UserLevelEnum.NORMAL, this);
}

@Override
public void doRefundByUser(OrderInfo refundVO) {
log.info("普通用户退款流程:需2个工作日到账");
// 调用订单维度模板逻辑
AbstractOrderRefundTemplate template = orderRefundComponent.getOrderTemplate(refundVO.getOrderType());
template.executeRefund();
}
}

# VIP 用户策略(VipUserRefundStrategy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class VipUserRefundStrategy implements IRefundUserStrategy {

@Autowired
private OrderRefundComponent orderRefundComponent;

@PostConstruct
private void initStrategy() {
RefundUserStrategyFactory.putStrategy(UserLevelEnum.VIP, this);
}

@Override
public void doRefundByUser(OrderInfo refundVO) {
log.info("VIP用户退款流程:第三方垫付立即到账");
// 调用订单维度模板逻辑
AbstractOrderRefundTemplate template = orderRefundComponent.getOrderTemplate(refundVO.getOrderType());
template.executeRefund();
}
}

# 策略工厂(RefundUserStrategyFactory)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class RefundUserStrategyFactory {
private static final Map<UserLevelEnum, IRefundUserStrategy> strategyMap = new HashMap<>();

// 获取策略
public static IRefundUserStrategy getStrategy(UserLevelEnum userLevel) {
return strategyMap.get(userLevel);
}

// 注册策略(通过@PostConstruct注入)
public static void putStrategy(UserLevelEnum userLevel, IRefundUserStrategy strategy) {
strategyMap.put(userLevel, strategy);
}
}

# 3. 模板模式实现(订单退款模板)

# 抽象模板类(AbstractOrderRefundTemplate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractOrderRefundTemplate {

// 模板方法:定义固定流程
public final void executeRefund() {
checkOrder(); // 公共逻辑:订单校验
updateRefundRecord(); // 公共逻辑:更新退款表
calculateEmptyRunFee();// 差异化逻辑:由子类实现
doFinalRefund(); // 差异化逻辑:由子类实现
}

// 公共方法
protected void checkOrder() {
log.info("订单校验通过");
}

protected void updateRefundRecord() {
log.info("退款记录更新至数据库");
}

// 抽象方法(子类实现)
protected abstract void calculateEmptyRunFee();
protected abstract void doFinalRefund();
}

# 普通订单模板(NormalOrderRefundTemplate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class NormalOrderRefundTemplate extends AbstractOrderRefundTemplate {

@Override
protected void calculateEmptyRunFee() {
OrderInfo order = OrderContext.getOrder();
double fee = order.getPaymentType() == 1
? Math.min(order.getAmount() * 0.1, 5) // 一段支付:比例计算,上限5元
: 10; // 二段支付:固定10元
log.info("普通订单空跑费计算:{}元", fee);
}

@Override
protected void doFinalRefund() {
log.info("普通订单退款完成,金额已退回用户账户");
}
}

# 团购订单模板(GroupOrderRefundTemplate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class GroupOrderRefundTemplate extends AbstractOrderRefundTemplate {

@Override
protected void calculateEmptyRunFee() {
// 团购订单无空跑费(差异化逻辑)
log.info("团购订单空跑费:0元");
}

@Override
protected void doFinalRefund() {
log.info("团购订单退款完成,金额原路返回");
}
}

# 订单模板管理组件(OrderRefundComponent)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class OrderRefundComponent {

private static final Map<OrderTypeEnum, AbstractOrderRefundTemplate> templateMap = new HashMap<>();

@Autowired
public void setTemplateMap(List<AbstractOrderRefundTemplate> templates) {
templates.forEach(template ->
templateMap.put(OrderTypeEnum.valueOf(template.getClass().getSimpleName().split("Refund")[0]), template)
);
}

public AbstractOrderRefundTemplate getOrderTemplate(OrderTypeEnum orderType) {
return templateMap.get(orderType);
}
}

# 4. 退款入口逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Service
public class RefundService {

public void processRefund(OrderInfo refundVO) throws Exception {
try {
// 1. 公共校验(可提取到模板公共方法)
checkBasicInfo(refundVO);

// 2. 根据用户等级获取策略
UserLevelEnum userLevel = UserLevelEnum.valueOf(refundVO.getUserId());
IRefundUserStrategy strategy = RefundUserStrategyFactory.getStrategy(userLevel);

// 3. 执行用户维度退款策略(包含订单维度逻辑)
strategy.doRefundByUser(refundVO);

} catch (Exception e) {
log.error("退款异常:{}", e.getMessage(), e);
throw new RefundException("退款失败");
}
}

private void checkBasicInfo(OrderInfo refundVO) {
// 基础参数校验(用户ID、订单ID等)
if (refundVO.getUserId() == null || refundVO.getOrderId() == null) {
throw new IllegalArgumentException("必要参数缺失");
}
}
}

# 四、模式协作关系

  1. 策略模式:通过 UserLevelEnum 区分用户类型,不同策略类( NormalUserRefundStrategy / VipUserRefundStrategy )处理差异化的用户退款流程。
  2. 模板模式AbstractOrderRefundTemplate 定义公共流程(订单校验、更新记录),具体订单模板(普通 / 团购订单)实现差异化的空跑费计算和最终退款逻辑。
  3. 工厂模式RefundUserStrategyFactoryOrderRefundComponent 作为容器,通过枚举映射策略和模板实例,避免硬编码 if-else

# 五、优化效果

  • 代码结构清晰:移除大量 if-else ,逻辑按维度(用户 / 订单)分离,可读性提升。
  • 易于扩展:新增订单类型或用户策略时,只需新增对应策略类 / 模板类,符合开闭原则
  • 复用性强:公共逻辑(订单校验、数据库操作)在模板类中统一实现,减少代码冗余。

# 六、总结

当系统中存在多维度差异化逻辑(如用户等级、订单类型)且包含公共流程时,可组合使用策略模式(处理横向差异化)、模板模式(处理纵向公共流程)和工厂模式(管理对象实例)。这种组合模式能有效解决 if-else 爆炸问题,提升代码可维护性和扩展性。

实践建议

  1. 优先识别业务中的 “变” 与 “不变” 部分,“不变” 逻辑抽象为模板,“变” 的部分封装为策略。
  2. 结合 Spring 框架的依赖注入和初始化注解( @PostConstruct ),简化工厂模式的实例注册流程。
  3. 复杂场景可引入枚举类统一管理类型标识,避免魔法值污染代码。