Java设计模式实战:一个支付模块的重构之旅,层层递进理解设计模式精髓

前言

设计模式不是背出来的,而是在解决实际问题的过程中自然浮现的。很多同学学设计模式时,往往陷入"每个模式都认识,但不知道什么时候用"的困境。

本文将通过一个电商支付模块的演进史 ,带你体验代码从"能跑"到"优雅"的全过程。我们会从一个满是 if-else 的原始版本开始,随着新需求的不断加入,一步步引入策略、工厂、单例、观察者、代理、模板方法、装饰器模式。每一步都是因为遇到了具体的痛点,才引入对应的模式。

主线:一个支付模块的重构之旅。

原则:先写能跑的代码,再重构出优雅的架构。


阶段一:原始版本------if-else 的泥潭

假设我们接到一个需求:实现订单支付,支持支付宝、微信、银联三种方式。

初始代码

java 复制代码
public class PayService {
    public void pay(String type, BigDecimal amount) {
        if ("ali".equals(type)) {
            System.out.println("支付宝支付: " + amount);
            // 调用支付宝SDK...
        } else if ("wechat".equals(type)) {
            System.out.println("微信支付: " + amount);
            // 调用微信SDK...
        } else if ("union".equals(type)) {
            System.out.println("银联支付: " + amount);
            // 调用银联SDK...
        } else {
            throw new IllegalArgumentException("不支持的支付类型: " + type);
        }
    }
}

问题分析

  • 新增支付方式要修改 pay 方法,违反开闭原则
  • 所有逻辑堆在一个方法里,难以测试难以维护
  • 支付方式越多,if-else 越臃肿

痛点:算法(支付逻辑)与使用逻辑耦合,需要将变化的部分抽离。


阶段二:策略模式------消除 if-else

解决方案

将每种支付方式封装成独立的策略类,实现同一个接口。

定义策略接口

java 复制代码
public interface PayStrategy {
    /**
     * 执行支付
     * @param amount 支付金额
     * @return 支付结果
     */
    PayResult pay(BigDecimal amount);

    /**
     * 返回该策略支持的支付类型
     */
    String getType();
}

定义支付结果

java 复制代码
public class PayResult {
    private boolean success;
    private String tradeNo;      // 交易流水号
    private String message;

    public PayResult(boolean success, String tradeNo, String message) {
        this.success = success;
        this.tradeNo = tradeNo;
        this.message = message;
    }

    public static PayResult success(String tradeNo) {
        return new PayResult(true, tradeNo, "支付成功");
    }

    public static PayResult fail(String message) {
        return new PayResult(false, null, message);
    }

    // getter 省略
}

以支付宝策略实现为例

java 复制代码
@Service
public class AliPayStrategy implements PayStrategy {

    private static final Logger log = LoggerFactory.getLogger(AliPayStrategy.class);

    @Override
    public String getType() {
        return "ali";
    }

    @Override
    public PayResult pay(BigDecimal amount) {
        try {
            log.info("开始支付宝支付,金额: {}", amount);
            // 1. 组装请求参数
            // 2. 调用支付宝 SDK
            // AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            // ...
            String tradeNo = "ALI_" + System.currentTimeMillis();
            log.info("支付宝支付成功,流水号: {}", tradeNo);
            return PayResult.success(tradeNo);
        } catch (Exception e) {
            log.error("支付宝支付失败", e);
            return PayResult.fail("支付宝支付失败: " + e.getMessage());
        }
    }
}

PayService 改为持有策略:

java 复制代码
@Service
public class PayService {
    private final PayStrategy payStrategy;

    public PayService(PayStrategy payStrategy) {
        this.payStrategy = payStrategy;
    }

    public PayResult pay(BigDecimal amount) {
        return payStrategy.pay(amount);
    }
}

效果

  • 新增支付方式只需新增一个实现类,符合开闭原则
  • 每个策略独立,易于单元测试
  • 消除了 if-else

但新问题来了:谁来创建具体的策略?客户端需要知道所有策略类,并手动选择。如果策略很多,选择逻辑又会变成新的 if-else


阶段三:工厂模式------统一策略创建(重点详解)

这是整个重构过程中最关键的一步,也是坑最多的一个环节。我会从最朴素的实现开始,一步步演进到生产级方案。

3.1 为什么需要工厂?

策略模式解决了"算法可替换",但留下了一个新问题:调用方怎么知道该用哪个策略?

java 复制代码
// 调用方被迫做选择
PayStrategy strategy;
if ("ali".equals(type)) {
    strategy = new AliPayStrategy();
} else if ("wechat".equals(type)) {
    strategy = new WechatPayStrategy();
} else if ("union".equals(type)) {
    strategy = new UnionPayStrategy();
}
strategy.pay(amount);

if-elsePayService 转移到了调用方,问题并没有真正解决。工厂模式的价值就在这里:把"选择哪个实现"的逻辑集中管理,让调用方只关心"我要什么"。

3.2 第一版:简单工厂(不推荐)

最朴素的写法:

java 复制代码
public class PayStrategyFactory {
    public static PayStrategy get(String type) {
        switch (type) {
            case "ali":
                return new AliPayStrategy();
            case "wechat":
                return new WechatPayStrategy();
            case "union":
                return new UnionPayStrategy();
            default:
                throw new IllegalArgumentException("不支持的支付类型: " + type);
        }
    }
}

调用方变成:

java 复制代码
PayStrategy strategy = PayStrategyFactory.get(type);
strategy.pay(amount);

问题很明显

  1. switch 又回来了,每次新增支付方式要改工厂,违反开闭原则
  2. new 出来的对象无法享受 Spring 的依赖注入(比如策略里需要注入 PayConfig
  3. 策略类如果有依赖,工厂管不了

所以简单工厂只适合"策略无依赖、类型极少"的场景,实际项目中很少用。

3.3 第二版:工厂 + 手动注册

让策略自己注册到工厂:

java 复制代码
@Component
public class PayStrategyFactory {
    private final Map<String, PayStrategy> strategyMap = new ConcurrentHashMap<>();

    public void register(String type, PayStrategy strategy) {
        strategyMap.put(type, strategy);
    }

    public PayStrategy get(String type) {
        PayStrategy strategy = strategyMap.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的支付类型: " + type);
        }
        return strategy;
    }
}

策略类主动注册:

java 复制代码
@Service
public class AliPayStrategy implements PayStrategy {
    @Autowired
    private PayStrategyFactory factory;

    @PostConstruct
    public void init() {
        factory.register("ali", this);
    }

    @Override
    public String getType() {
        return "ali";
    }

    @Override
    public PayResult pay(BigDecimal amount) {
        System.out.println("支付宝支付: " + amount);
        return PayResult.success("ALI_" + System.currentTimeMillis());
    }
}

问题

  1. 每个策略类都要写一遍注册代码,重复
  2. 策略和工厂循环依赖(策略注入工厂,工厂持有策略)
  3. 注册的 key("ali")硬编码在策略里,容易写错

这个版本比第一版好,但还不够优雅。

3.4 第三版:工厂 + 自定义注解 + Spring 自动注入(推荐)

这是生产环境中最常用的方案。核心思路:用注解标记策略类型,Spring 注入所有策略,工厂通过反射读取注解建立映射。

Step 1:定义注解

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component  // 关键:让 Spring 自动扫描
public @interface PayType {
    String value();
}

注意 @Component 元注解,这样加了 @PayType 的类会被 Spring 自动扫描,不需要再加 @Service

Step 2:策略实现(改造后的完整代码)

支付宝策略

java 复制代码
@PayType("ali")
public class AliPayStrategy implements PayStrategy {

    private static final Logger log = LoggerFactory.getLogger(AliPayStrategy.class);

    @Autowired
    private PayConfig payConfig;  // 享受 Spring 依赖注入

    @Override
    public String getType() {
        return "ali";
    }

    @Override
    public PayResult pay(BigDecimal amount) {
        try {
            log.info("支付宝支付开始,appId={}, 金额={}", payConfig.getAppId(), amount);

            // 1. 参数校验
            if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
                return PayResult.fail("支付金额必须大于0");
            }

            // 2. 组装请求参数
            // AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            // request.setBizContent(...);

            // 3. 调用支付宝 SDK
            // String result = AlipayClientFactory.get().pageExecute(request).getBody();

            // 4. 解析结果
            String tradeNo = "ALI_" + System.currentTimeMillis();
            log.info("支付宝支付成功,流水号: {}", tradeNo);
            return PayResult.success(tradeNo);

        } catch (Exception e) {
            log.error("支付宝支付异常", e);
            return PayResult.fail("支付宝支付异常: " + e.getMessage());
        }
    }
}

微信策略

java

kotlin 复制代码
@PayType("wechat")
public class WechatPayStrategy implements PayStrategy {

    private static final Logger log = LoggerFactory.getLogger(WechatPayStrategy.class);

    @Autowired
    private PayConfig payConfig;

    @Override
    public String getType() {
        return "wechat";
    }

    @Override
    public PayResult pay(BigDecimal amount) {
        try {
            log.info("微信支付开始,mchId={}, 金额={}", payConfig.getMchId(), amount);

            if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
                return PayResult.fail("支付金额必须大于0");
            }

            // 1. 组装请求参数
            // WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            // request.setTotalFee(amount.multiply(new BigDecimal("100")).intValue());

            // 2. 调用微信支付 SDK
            // WxPayUnifiedOrderResult result = wxPayService.unifiedOrder(request);

            // 3. 解析结果
            String tradeNo = "WX_" + System.currentTimeMillis();
            log.info("微信支付成功,流水号: {}", tradeNo);
            return PayResult.success(tradeNo);

        } catch (Exception e) {
            log.error("微信支付异常", e);
            return PayResult.fail("微信支付异常: " + e.getMessage());
        }
    }
}

银联策略

java 复制代码
@PayType("union")
public class UnionPayStrategy implements PayStrategy {

    private static final Logger log = LoggerFactory.getLogger(UnionPayStrategy.class);

    @Override
    public String getType() {
        return "union";
    }

    @Override
    public PayResult pay(BigDecimal amount) {
        try {
            log.info("银联支付开始,金额={}", amount);

            if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
                return PayResult.fail("支付金额必须大于0");
            }

            // 1. 组装请求参数
            // 2. 调用银联 SDK
            // 3. 解析结果

            String tradeNo = "UNION_" + System.currentTimeMillis();
            log.info("银联支付成功,流水号: {}", tradeNo);
            return PayResult.success(tradeNo);

        } catch (Exception e) {
            log.error("银联支付异常", e);
            return PayResult.fail("银联支付异常: " + e.getMessage());
        }
    }
}

Step 3:工厂实现

java 复制代码
@Component
public class PayStrategyFactory {

    private static final Logger log = LoggerFactory.getLogger(PayStrategyFactory.class);

    private final Map<String, PayStrategy> strategyMap = new ConcurrentHashMap<>();

    // Spring 注入所有 PayStrategy 实现
    public PayStrategyFactory(List<PayStrategy> strategies) {
        if (strategies == null || strategies.isEmpty()) {
            throw new IllegalStateException("未找到任何 PayStrategy 实现,请检查包扫描路径");
        }

        for (PayStrategy strategy : strategies) {
            // 关键:用 AopUtils 处理被代理的类
            Class<?> targetClass = AopUtils.getTargetClass(strategy);
            PayType annotation = AnnotationUtils.findAnnotation(targetClass, PayType.class);

            if (annotation == null) {
                throw new IllegalStateException(
                    targetClass.getName() + " 缺少 @PayType 注解");
            }

            String type = annotation.value();
            if (type == null || type.trim().isEmpty()) {
                throw new IllegalStateException(
                    targetClass.getName() + " @PayType 的值不能为空");
            }

            PayStrategy existing = strategyMap.putIfAbsent(type, strategy);
            if (existing != null) {
                throw new IllegalStateException(
                    "支付类型 [" + type + "] 重复注册: "
                    + existing.getClass().getName() + " 和 " + targetClass.getName());
            }

            log.info("注册支付策略: type={}, class={}", type, targetClass.getSimpleName());
        }

        log.info("支付策略工厂初始化完成,共 {} 种支付方式: {}", 
                 strategyMap.size(), strategyMap.keySet());
    }

    /**
     * 根据类型获取策略
     */
    public PayStrategy get(String type) {
        PayStrategy strategy = strategyMap.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException(
                "不支持的支付类型: " + type + ",支持的类型: " + strategyMap.keySet());
        }
        return strategy;
    }

    /**
     * 返回所有支持的支付类型
     */
    public Set<String> supportedTypes() {
        return Collections.unmodifiableSet(strategyMap.keySet());
    }
}

Step 4:调用方

java 复制代码
@Service
public class PayService {

    private static final Logger log = LoggerFactory.getLogger(PayService.class);

    private final PayStrategyFactory payStrategyFactory;

    public PayService(PayStrategyFactory payStrategyFactory) {
        this.payStrategyFactory = payStrategyFactory;
    }

    public PayResult pay(String type, BigDecimal amount) {
        log.info("收到支付请求: type={}, amount={}", type, amount);

        PayStrategy strategy = payStrategyFactory.get(type);
        PayResult result = strategy.pay(amount);

        log.info("支付结果: success={}, tradeNo={}", result.isSuccess(), result.getTradeNo());
        return result;
    }
}

这个版本的优势

  • 真正的开闭原则 :新增支付方式只需加一个类 + @PayType 注解,零改动
  • 享受 Spring 依赖注入 :策略类里可以注入任意 Bean(如 PayConfig
  • 启动时校验 :重复注册、缺少注解会启动失败,问题暴露在编译期而非运行期
  • 类型安全:通过注解而非字符串硬编码
  • 可观测:启动时打印所有已注册的策略,排查问题方便

3.5 进阶:如果策略需要参数怎么办?

有些支付方式需要额外参数,比如微信支付需要区分"小程序"和"H5":

java 复制代码
@PayType("wechat")
public class WechatPayStrategy implements PayStrategy {
    // 如何区分小程序和 H5?
}

方案一:用多个 key

java 复制代码
@PayType("wechat_mini")
public class WechatMiniPayStrategy implements PayStrategy { ... }

@PayType("wechat_h5")
public class WechatH5PayStrategy implements PayStrategy { ... }

简单但不够灵活。

方案二:策略内部再路由(推荐)

java 复制代码
@PayType("wechat")
public class WechatPayStrategy implements PayStrategy {

    private final Map<String, WechatSubStrategy> subStrategies;

    public WechatPayStrategy(List<WechatSubStrategy> subStrategyList) {
        this.subStrategies = subStrategyList.stream()
            .collect(Collectors.toMap(WechatSubStrategy::getScene, s -> s));
    }

    @Override
    public PayResult pay(BigDecimal amount) {
        // 从上下文获取场景,或从 ThreadLocal
        String scene = PayContext.getScene();
        WechatSubStrategy sub = subStrategies.get(scene);
        if (sub == null) {
            throw new IllegalArgumentException("不支持的微信支付场景: " + scene);
        }
        return sub.pay(amount);
    }
}

方案三:用 SpEL 或条件注解

Spring 的 @ConditionalOnProperty 可以根据配置动态注册,但配置变更需要重启。

实际项目中,方案二最常用:把粗粒度的路由交给工厂,细粒度的路由交给策略内部。

3.6 工厂模式的三种形态

到这里有必要澄清一下:工厂模式其实有三种形态,很多人会混淆。

形态 特点 适用场景
简单工厂 一个方法根据参数返回不同对象 类型少、无依赖
工厂方法 每个产品对应一个工厂类 产品族扩展频繁
抽象工厂 一个工厂创建多个产品族 多维度变化

我们的支付模块用的是"简单工厂 + Spring 自动注册"的变体,这是实际项目中最实用的组合。

什么时候用工厂方法? 如果支付不只是"支付",还有"退款""查询""对账",每个操作都需要一套实现,那就适合抽象工厂:

java 复制代码
public interface PayFactory {
    PayStrategy createPay();
    RefundStrategy createRefund();
    QueryStrategy createQuery();
}

@PayType("ali")
public class AliPayFactory implements PayFactory {
    public PayStrategy createPay() { return new AliPayStrategy(); }
    public RefundStrategy createRefund() { return new AliRefundStrategy(); }
    public QueryStrategy createQuery() { return new AliQueryStrategy(); }
}

但要注意:抽象工厂的类数量会爆炸。 如果不是真的有"产品族"需求,不要用。

3.7 踩坑记录

坑1:List<PayStrategy> 注入为空

如果策略类没有被 Spring 扫描到,List 会是空的,工厂初始化时不会报错,运行时才 NullPointerException

解决方案 :在工厂构造函数里校验 strategies 是否为空。

java 复制代码
if (strategies == null || strategies.isEmpty()) {
    throw new IllegalStateException("未找到任何 PayStrategy 实现,请检查包扫描路径");
}

坑2:策略类有状态导致线程安全

Spring 默认单例,如果策略类有可变成员变量,会有线程安全问题。

java 复制代码
// ❌ 危险
@PayType("ali")
public class AliPayStrategy implements PayStrategy {
    private String currentOrderId;  // 成员变量,多线程共享
    
    public PayResult pay(BigDecimal amount) {
        this.currentOrderId = ...;  // 线程不安全
    }
}

// ✅ 正确
@PayType("ali")
public class AliPayStrategy implements PayStrategy {
    public PayResult pay(BigDecimal amount, PayContext context) {
        String orderId = context.getOrderId();  // 局部变量
    }
}

坑3:getClass().getAnnotation() 拿到的是 null

如果策略类被 CGLIB 代理(比如加了 @Transactional),getClass() 拿到的是代理类,注解可能读不到。

解决方案 :用 AopUtils.getTargetClass(strategy)AnnotationUtils.findAnnotation()

java 复制代码
PayType annotation = AnnotationUtils.findAnnotation(
    AopUtils.getTargetClass(strategy), PayType.class);

坑4:策略类名和注解值不一致

有人用类名做 key,有人用注解值做 key,混用会出问题。统一用注解值,并在工厂里做重复校验。

坑5:工厂和策略循环依赖

如果策略里注入了工厂(比如为了注册),会和工厂形成循环依赖。@LazyObjectProvider 解决 ,但更好的做法是策略不依赖工厂,注册逻辑完全由工厂的构造函数完成。

3.8 小结

工厂模式的演进过程,本质上是一个 "如何优雅地管理变化" 的过程:

  • 第一版:能跑,但违反开闭原则
  • 第二版:符合开闭,但重复代码多
  • 第三版:优雅,但需要理解 Spring 生命周期

没有一步到位,都是被需求逼出来的。 这也是我想传达的核心观点:设计模式不是设计出来的,是重构出来的。


阶段四:单例模式------全局配置与 Spring Bean

痛点

支付过程中需要读取商户号、密钥等配置,这些配置全局唯一,频繁创建浪费资源,且必须保证一致性。

解决方案

在 Spring 中,默认的 Bean 作用域就是单例,我们无需自己实现。但理解单例的几种写法,对面试和源码阅读都很有帮助。

java 复制代码
// 静态内部类实现单例(推荐)
public class PayConfig {
    private PayConfig() {}

    private static class Holder {
        private static final PayConfig INSTANCE = new PayConfig();
    }

    public static PayConfig getInstance() {
        return Holder.INSTANCE;
    }
}

在 Spring 中直接用 @Component

java 复制代码
@Component
public class PayConfig {
    @Value("${pay.appId}")
    private String appId;
    @Value("${pay.privateKey}")
    private String privateKey;
    @Value("${pay.mchId}")
    private String mchId;
    // getter
}

注意:单例 Bean 不要有可变成员变量,否则线程不安全。


阶段五:观察者模式------支付成功后的通知

痛点

支付成功后需要触发多个动作:发短信、加积分、记录日志。如果直接写在支付方法里,耦合严重。

解决方案

使用 Spring 的事件机制。

java 复制代码
// 1. 定义事件
public class OrderPaidEvent extends ApplicationEvent {
    private final Long orderId;
    private final BigDecimal amount;
    private final String tradeNo;

    public OrderPaidEvent(Object source, Long orderId, BigDecimal amount, String tradeNo) {
        super(source);
        this.orderId = orderId;
        this.amount = amount;
        this.tradeNo = tradeNo;
    }
    // getter
}

// 2. 发布事件
@Service
public class PayService {
    private final ApplicationEventPublisher publisher;

    public PayResult pay(String type, BigDecimal amount, Long orderId) {
        PayStrategy strategy = payStrategyFactory.get(type);
        PayResult result = strategy.pay(amount);
        
        if (result.isSuccess()) {
            publisher.publishEvent(new OrderPaidEvent(this, orderId, amount, result.getTradeNo()));
        }
        return result;
    }
}

// 3. 监听器
@Component
public class SmsListener {
    @EventListener
    @Async
    public void onOrderPaid(OrderPaidEvent event) {
        System.out.println("发送短信,订单号: " + event.getOrderId());
    }
}

@Component
public class PointListener {
    @EventListener
    @Async
    public void onOrderPaid(OrderPaidEvent event) {
        System.out.println("增加积分: " + event.getAmount());
    }
}

效果

  • 支付逻辑与后续动作完全解耦
  • 新增后续动作只需加监听器
  • 支持异步执行,不影响主流程

阶段六:代理模式------统一日志与事务

痛点

日志、事务、权限校验等横切关注点散落在各个业务方法中。

解决方案

使用 Spring AOP,底层就是动态代理。

java 复制代码
@Aspect
@Component
public class LogAspect {
    @Around("@annotation(com.example.Log)")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("方法执行前: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed();
        System.out.println("方法执行后: " + joinPoint.getSignature().getName());
        return result;
    }
}

在需要日志的方法上加 @Log 注解即可。

效果

  • 日志、事务、权限等统一处理
  • 业务方法只关注核心逻辑
  • 符合 AOP 思想

阶段七:模板方法模式------规范支付流程

痛点

每种支付的步骤细节可能不同,比如支付宝需要风控,微信不需要。如何规范流程?

解决方案

定义抽象类,固定支付流程,将差异步骤交给子类实现。

java 复制代码
public abstract class AbstractPayTemplate implements PayStrategy {

    // 模板方法,定义流程
    public final PayResult execute(BigDecimal amount) {
        validate(amount);
        riskCheck(amount);      // 钩子方法,子类可覆盖
        PayResult result = doPay(amount);
        if (result.isSuccess()) {
            record(result);
            notifySuccess(result);
        }
        return result;
    }

    protected abstract void validate(BigDecimal amount);
    protected abstract PayResult doPay(BigDecimal amount);

    // 默认实现,子类可选覆盖
    protected void riskCheck(BigDecimal amount) {
        System.out.println("默认风控检查");
    }

    protected void record(PayResult result) {
        System.out.println("记录支付流水: " + result.getTradeNo());
    }

    protected void notifySuccess(PayResult result) {
        System.out.println("发送支付成功通知");
    }
}

支付宝实现:

java 复制代码
@PayType("ali")
public class AliPayTemplate extends AbstractPayTemplate {

    @Override
    public String getType() {
        return "ali";
    }

    @Override
    protected void validate(BigDecimal amount) {
        System.out.println("支付宝参数校验");
    }

    @Override
    protected PayResult doPay(BigDecimal amount) {
        System.out.println("支付宝扣款: " + amount);
        return PayResult.success("ALI_" + System.currentTimeMillis());
    }

    @Override
    protected void riskCheck(BigDecimal amount) {
        System.out.println("支付宝风控检查");
    }
}

效果

  • 流程统一,避免遗漏步骤
  • 子类只关注差异,代码复用

阶段八:装饰器模式------动态增强支付功能

痛点

支付请求可能需要加密、签名,响应需要解密。这些功能可以动态添加,不影响核心支付逻辑。

解决方案

用装饰器包装支付策略。

java 复制代码
public abstract class PayDecorator implements PayStrategy {
    protected final PayStrategy delegate;

    public PayDecorator(PayStrategy delegate) {
        this.delegate = delegate;
    }

    @Override
    public String getType() {
        return delegate.getType();
    }
}

public class EncryptDecorator extends PayDecorator {

    public EncryptDecorator(PayStrategy delegate) {
        super(delegate);
    }

    @Override
    public PayResult pay(BigDecimal amount) {
        System.out.println("加密支付参数");
        PayResult result = delegate.pay(amount);
        System.out.println("解密支付结果");
        return result;
    }
}

使用:

java 复制代码
PayStrategy strategy = payStrategyFactory.get("ali");
if (merchant.needEncrypt()) {
    strategy = new EncryptDecorator(strategy);
}
strategy.pay(amount);

效果

  • 动态添加功能,不影响原有类
  • 比继承更灵活,可以任意组合

总结:设计模式如何协同工作

回顾整个演进过程,我们并不是一开始就设计好所有模式,而是遇到问题 -> 重构 -> 引入模式。每个模式都解决了前一个阶段的痛点:

阶段 问题 引入模式
1 if-else 膨胀 策略模式
2 策略创建复杂 工厂模式
3 配置全局唯一 单例模式
4 支付后多动作耦合 观察者模式
5 横切逻辑散落 代理模式
6 流程不统一 模板方法模式
7 功能需动态增强 装饰器模式

最终架构中,这些模式各司其职:

  • 工厂 + 策略:选择支付方式(本文重点)
  • 单例:管理全局配置
  • 观察者:解耦支付后通知
  • 代理:统一日志事务
  • 模板方法:规范支付流程
  • 装饰器:动态增强功能

设计模式不是孤立的,它们常常组合出现。但切记:不要为了用而用,过度设计比不设计更可怕。

写在最后

好的代码不是设计出来的,是重构出来的。先让代码跑起来,再让它优雅起来。

当你下次遇到 if-else 堆积时,想想策略模式;当创建逻辑复杂时,想想工厂模式;当需要解耦时,想想观察者模式......但永远记住:模式是手段,不是目的。

如果这篇文章对你有帮助,欢迎点赞收藏。

相关推荐
江湖十年1 小时前
Go 还是 Golang?可能你一直都搞错了!
后端·面试·go
计算机毕设定制辅导-无忧学长1 小时前
《基于SpringBoot的庭院玫瑰栽培养护知识交互式科普平台设计与实现》
java·spring boot·后端·毕业设计·个性化推荐·协同过滤算法·庭院玫瑰栽培养护知识科普平台
骇客野人1 小时前
SpringBoot数十Jar包批量生产部署落地实施方案(Shell+Systemd完整版)
spring boot·后端·jar
Json____2 小时前
从零构建家政服务平台:一套全栈架构如何打通管理端与移动端-java-springboot
java·spring boot·后端·架构·毕设·wwwoop.com
AI云海2 小时前
计算机视觉之YOLO11整体架构、多任务能力
人工智能·计算机视觉·架构
leeyi2 小时前
命令行里的平台:df_cli 都会什么(第108篇)
后端·aigc·agent
云雀衔光3 小时前
Java Spring AI MCP:在 Spring 里把 AI 接上你的业务系统
java·开发语言·人工智能·后端·测试工具·spring
美狐美颜SDK开放平台3 小时前
从视频处理到实时渲染:直播APP中视频美颜SDK的关键技术解析
前端·人工智能·架构·实时互动·音视频·视频美颜sdk
苏渡苇3 小时前
Spring Insight 里如何对 Span 进行清洗
java·spring boot·后端·spring·系统监控