前言
设计模式不是背出来的,而是在解决实际问题的过程中自然浮现的。很多同学学设计模式时,往往陷入"每个模式都认识,但不知道什么时候用"的困境。
本文将通过一个电商支付模块的演进史 ,带你体验代码从"能跑"到"优雅"的全过程。我们会从一个满是 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-else 从 PayService 转移到了调用方,问题并没有真正解决。工厂模式的价值就在这里:把"选择哪个实现"的逻辑集中管理,让调用方只关心"我要什么"。
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);
问题很明显:
switch又回来了,每次新增支付方式要改工厂,违反开闭原则new出来的对象无法享受 Spring 的依赖注入(比如策略里需要注入PayConfig)- 策略类如果有依赖,工厂管不了
所以简单工厂只适合"策略无依赖、类型极少"的场景,实际项目中很少用。
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());
}
}
问题:
- 每个策略类都要写一遍注册代码,重复
- 策略和工厂循环依赖(策略注入工厂,工厂持有策略)
- 注册的 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:工厂和策略循环依赖
如果策略里注入了工厂(比如为了注册),会和工厂形成循环依赖。用 @Lazy 或 ObjectProvider 解决 ,但更好的做法是策略不依赖工厂,注册逻辑完全由工厂的构造函数完成。
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 堆积时,想想策略模式;当创建逻辑复杂时,想想工厂模式;当需要解耦时,想想观察者模式......但永远记住:模式是手段,不是目的。
如果这篇文章对你有帮助,欢迎点赞收藏。