策略模式适用于算法逻辑迥异、需频繁增删且调用方不关心细节的场景,如支付网关或通知渠道;数值微调宜用配置或枚举,硬套反增成本;必须定义接口约束方法签名,上下文只传入不创建策略,策略对象须无状态。

策略模式不是“学完就能套用”的模板,而是当你写完一个 if 分支、new 出第三个相似类、或测试时发现两个策略行为互相污染,才该立刻停下来想:这里是不是该上策略?
为什么你的 if-else 不能直接改成策略模式
很多人一上来就照着示例写 DiscountStrategy 接口和三个实现类,结果发现代码更难读了——因为没识别出真正的「变化点」。
- 真正适合策略的,是那些「算法逻辑完全不同、未来大概率要增删、且调用方不关心细节」的场景,比如支付网关(
AlipayStrategyvsWechatPayStrategy)、通知渠道(SmsNotifiervsEmailNotifier) - 如果只是数值微调(如
$price * 0.9和$price * 0.85),用配置表或枚举更轻量;硬套策略反而增加维护成本 - 接口方法签名必须严格一致:所有策略的
calculate()必须接收相同类型参数(如array{id: string, amount: float}),不能一个收int、一个收Order对象
PHP里策略类必须实现接口,不能只靠约定
PHP 是动态语言,但策略模式失效最常见的原因,就是漏掉接口约束。
- 没定义
interface PaymentStrategy,而是让几个类各自写个pay()方法——运行时调用$strategy->pay()会直接报Fatal error: Call to undefined method - 别用抽象类代替接口,除非真有共用逻辑(比如统一日志埋点)。抽象类会强制继承,破坏策略间的正交性
- PHP 8+ 推荐用联合类型或具体 DTO 类型声明参数,比如
public function process(Order $order): bool,避免用mixed导致各策略对输入结构理解不一致
上下文类(Context)只传入、不创建、不判断
PaymentProcessor 这类上下文,本质是个“策略转接头”,不是调度中心。
立即学习“PHP免费学习笔记(深入)”;
- 构造函数必须接受策略实例:
public function __construct(PaymentStrategy $strategy),而不是在内部new AlipayStrategy() - 不要在上下文中写
if ($env === 'prod') { new RealStrategy(); } else { new MockStrategy(); }——那是工厂或 DI 容器的事 - 需要运行时切换?加个
setStrategy(PaymentStrategy $s)就够了,别塞进业务逻辑分支里 - 策略对象必须无状态:禁止用
static $cache、全局变量、或在构造时偷偷存用户 ID。否则 CLI 命令或 Swoole 长连接下极易出现数据错乱
最容易被忽略的边界问题
策略模式真正难的不是写类,而是守住三道线:
- 策略只管「怎么算」,不管「谁来选」——选哪个策略由工厂、路由层或配置驱动
- 策略之间不共享数据,也不依赖彼此——每个策略应能单独单元测试通过
- PHP-FPM 下单次请求内策略实例唯一,但别指望它跨请求缓存状态;要持久化?交给
Redis或APCu,不是策略本身



















