硬编码if-else会导致分支嵌套深、测试难覆盖、新增类型需改主流程、计算与校验耦合;策略模式通过couponstrategy接口解耦计算、校验、库存逻辑,结合公共校验服务、结构化返回值及php运行时优化实现真正复用。

优惠券类型多变时,为什么硬编码 if-else 会迅速失控
当优惠券类型从「满100减10」扩展到「满200打8折」「买A赠B」「限品类N折」「阶梯满减」时,用 if/elseif 堆叠判断逻辑会直接导致:分支嵌套深、测试难覆盖、新增类型要改主流程、折扣计算和条件校验混在一起。这不是扩展性问题,是维护性灾难。
策略模式不是为“设计感”而加的,是为把「谁来算折扣」「谁来验资格」「谁来占库存」这三件事拆开,让每种优惠券只关心自己的规则。
-
CouponStrategy接口统一定义calculateDiscount($order, $coupon)和validate($order, $coupon) - 每个具体策略类(如
FixedAmountOffStrategy)只实现自己那一块,不碰其他类型逻辑 - 主流程通过
$strategy = $this->strategyFactory->get($coupon->type)拿实例,完全解耦
如何让策略能真正复用,而不是换个名字又写一遍
很多团队落地策略模式后发现,不同策略里重复出现「检查用户是否在白名单」「判断商品是否参与活动」「过滤已下架SKU」——这些不该散落在各个策略内部。
把公共校验提前抽成独立服务,比如 CouponValidator,再让策略按需组合:
- 所有策略都先调用
$validator->commonRules($order, $coupon)(基础时效、状态、用户等级) - 再各自执行差异化校验:
$strategy->validateSpecific($order)(如「仅限新客」或「不含特价品」) - 折扣计算也分层:通用部分(如叠加限制、互斥规则)由上下文控制,策略只返回「本次应扣金额」
否则所谓策略,只是把 if 搬进类里,没解决本质问题。
折扣结果必须可逆,否则财务对账会出事
用户取消订单、退货、修改地址触发重新计算时,如果策略只返回一个数字,你无法知道这个 15.8 元是怎么来的:是满减凑单产生的?还是跨店合并导致的?有没有用掉赠券?
策略的 calculateDiscount() 必须返回结构化数据,至少包含:
-
amount:实际抵扣金额(必须是正数,单位:分) -
appliedItems:哪些商品/子订单参与了该优惠(数组,含item_id和matchedRule) -
breakdown:明细(如「满300减30中使用了280元订单金额,抵扣28元」)
没有 breakdown,运营查不出为什么某笔订单没生效;没有 appliedItems,退货时无法精准释放优惠额度。
PHP 实现时要注意的三个运行时陷阱
策略模式在 PHP 中跑不起来,往往卡在细节:
- 别用
new $className()动态实例化策略——类不存在时 fatal error,且无法做依赖注入;改用容器(如Container::get($strategyId))或工厂方法 - 策略类里避免直接读 DB 或调远程 API;把数据提前加载好传入
calculateDiscount($orderData, $couponData),否则单元测试根本没法写 - 缓存策略实例本身没意义,但
$coupon->type到策略类名的映射可以缓存(APCu 或 Redis),避免每次array_key_exists查表
策略模式的价值不在“用了设计模式”,而在让每一次优惠变更都能控制在两个文件内:一个策略类 + 一个配置项。其余地方不动,上线风险才可控。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











