先抛一个反直觉的结论:策略模式最重要的价值不是帮你少写几个 if-else,而是把“变化”和“稳定”在代码里分开,让变化的部分可以在运行期被替换,同时让稳定的部分稳如磐石。
这些年看过的项目里,真正把策略模式用好的,代码读起来像一份清晰的说明书;用不好的,就是给一个本来很简单的问题套了一层又一层抽象,最后维护的人想骂人。所以这篇文章我不想只贴一段策略模式的教科书代码,而是想聊清楚:策略模式到底在解决什么问题、它的边界在哪里、为什么很多场景下你其实不需要它,以及它和工厂、状态模式这些“亲戚”到底怎么区分。最后会结合最近的子代理(subagent)设计范式,聊聊策略模式在新场景里是不是过时了。
这个内容适合几类人:正在学设计模式的学生,尤其是对着期末作业或大作业不知道怎么落地的;写了两三年业务代码、发现自己的 if-else 越堆越长但不敢乱动的开发者;以及那些已经在用策略模式,但偶尔会犹豫“这里到底该不该用策略”的工程师。我会尽量用大白话把原理讲透,代码部分会给出 Java 和 C++ 两版,方便不同技术栈的读者对照。
1. 策略模式真正要解决的问题:不是代码重复,是变化点的失控
很多人对策略模式的第一印象是“消除 if-else”。这个说法不算错,但特别容易误导人。因为 if-else 本身不是坏东西,在分支很少、逻辑很稳定的情况下,if-else 反而是最直观、最好读的写法。真正让代码变烂的,是那些“不确定未来还会加多少分支”的 if-else,以及多个地方重复出现同一组分支判断。
1.1 从一段真实的促销报价代码说起
我举个例子,假设你在做一个电商系统的报价模块,最初的需求很简单:普通用户原价购买,VIP 用户打 9 折,企业用户打 8 折。很多人的第一版代码可能长这样:
java复制public double calculatePrice(String userType, double originalPrice) {
if ("vip".equals(userType)) {
return originalPrice * 0.9;
} else if ("enterprise".equals(userType)) {
return originalPrice * 0.8;
} else {
return originalPrice;
}
}
这段代码有问题吗?如果需求永远只有这样三种用户,那真没什么问题——简单、直接、任何一个新人都能看懂。但现实是,这种貌似“简单”的代码往往会在三个月后变成这样:
java复制public double calculatePrice(String userType, double originalPrice, int orderCount, boolean isFirstOrder, String couponId, ...) {
if ("vip".equals(userType)) {
if (orderCount > 10) {
return originalPrice * 0.85;
}
return originalPrice * 0.9;
} else if ("enterprise".equals(userType)) {
if (isFirstOrder) {
// 和优惠券逻辑纠缠在一起
}
return originalPrice * 0.8;
} else {
// 普通用户又拆出新客、老客、复购等多种情况
}
}
你会发现,每个新需求都在往这个函数里塞新的参数、新的分支。代码的“圈复杂度”越来越高,逻辑开始纠缠,测试用例越来越难写,而真正可怕的还不是这里——而是当“是否有首单优惠”这个判断被复制到了订单列表、购物车、结算页三个地方时,你改一个逻辑漏改另一个,线上就出事。
1.2 策略模式提供的是一种“封闭的变化点”思维
策略模式的核心思想可以概括成一句话:把算法(或者说行为)封装成独立的策略对象,让使用算法的“上下文”不感知具体算法,从而可以用组合的方式在运行时替换算法。
这里的关键不是“少写分支”,而是“把分支消灭在对象的创建阶段”。分支仍然存在——你总得决定创建哪一个策略对象——但它只出现一次,而不是散落在业务逻辑的各个角落。这就是所谓的“封装变化点”。
我用一个生活化的类比来解释。你去餐厅吃饭,菜单上有红烧肉、清蒸鱼、麻婆豆腐。如果餐厅把“点菜”这个动作和“做菜”的过程写在同一个本子上,每增加一道新菜就要改一次点菜流程。而策略模式的做法是:菜单负责“展示菜名和价格”,后厨负责“实际做菜”,两者通过一个“下单”接口对接。客人选了红烧肉,菜单把这个选择传递给做红烧肉的厨师,仅此而已。就算后厨明天换一个厨师,菜名和价格不用跟着变。
这个过程里,发生变化的是“菜”——也就是算法;保持稳定的是“菜单”——也就是上下文。策略模式为我们提供了一条清晰的边界:那些未来可能扩展的算法,应该被拿出来独立演化和测试。
1.3 什么时候你不需要策略模式
正因为策略模式被过度神化了,我得先泼一盆冷水。如果你的代码只有两三个分支,而且未来几年内都不太可能扩展,那就老老实实写 if。设计模式不是勋章,每多一层抽象,就多一层学习成本和间接跳转成本。真正的高手懂得在“简单”和“可扩展”之间找平衡。
我还见过一种反模式:一个接口只有一个实现类,也硬要套上策略模式,理由是“万一以后有第二个实现呢”。这种“万一”如果持续三年都没有发生,那这三年的代码就是在为一个从未出现的需求付房租。更好的做法是:当第二个相似场景出现时,再顺手重构。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 策略模式的经典实现拆解:一份能直接抄作业的 Java 和 C++ 对照
讲完理论,下面给一个可以照着写的完整实现。我会用“支付方式”这个场景来演示,因为它比报价系统更贴近大多数人日常写的业务代码。需求是:一个订单支持支付宝、微信、银行卡三种支付方式,未来可能还会加“余额支付”。
2.1 首先是策略接口和三个具体策略
不管是 Java 还是 C++,策略模式的结构都一样:一个策略接口(或抽象类)、若干具体策略类、一个持有策略引用的上下文。先看 Java 版:
java复制// 策略接口:所有支付方式的统一契约
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
// 具体策略:支付宝支付
public class AlipayStrategy implements PaymentStrategy {
private Str
