直接new具体策略类会导致业务逻辑与算法强耦合,应通过策略接口+工厂模式解耦;接口定义统一方法签名,工厂根据标识返回实现类实例,php 8+推荐联合类型与枚举提升类型安全。

为什么直接 new 具体策略类会埋下维护雷
硬编码 new DiscountStrategyV2() 或 new TaxCalculationLegacy(),意味着业务逻辑和算法实现强耦合。一旦要切换折扣规则(比如从“满300减50”换成“会员等级折扣”),就得改所有调用点——这不是扩展,是修补。接口不是摆设,它定义的是「能做什么」,而不是「怎么去做」。
- 策略接口必须声明统一方法签名,例如
calculate($amount, $context),所有实现类不得增减参数、不得改返回类型 - 不要在接口里加非核心方法(如
logUsage()),否则会迫使无关策略也实现它 - PHP 8+ 建议用联合类型或
mixed明确输入输出边界,避免运行时类型错位
如何用工厂 + 接口解耦策略获取逻辑
把“选哪个策略”的决策从业务代码里拎出来,交给一个简单工厂。它不负责算法,只负责根据条件返回符合 PaymentStrategy 接口的实例。
interface PaymentStrategy {
public function process(float $amount): bool;
}
<p>class AlipayStrategy implements PaymentStrategy { /<em> ... </em>/ }
class WechatPayStrategy implements PaymentStrategy { /<em> ... </em>/ }</p><p>class PaymentStrategyFactory {
public static function get(string $channel): PaymentStrategy {
return match($channel) {
'alipay' => new AlipayStrategy(),
'wechat' => new WechatPayStrategy(),
default => throw new InvalidArgumentException("Unknown channel: {$channel}"),
};
}
}</p>
- 工厂方法用
static即可,无需单例——策略本身无状态,每次 new 都安全 - 避免在工厂里做复杂判断(比如查数据库决定用哪个策略),那已超出策略模式职责,应由上层传入明确标识
- 如果策略需依赖外部服务(如 Redis 缓存配置),通过构造函数注入,别在工厂里 new 它们
策略类里该放什么、不该放什么
策略不是万能工具箱。它只封装「当前这个算法分支」的全部行为,其他一切交给调用方或组合对象。
- ✅ 可以:校验输入有效性(如
if ($amount ) - ✅ 可以:调用本策略专用的辅助函数(如
$this->applyCouponCode($code)) - ❌ 不要:读取全局配置项(
$_ENV['DISCOUNT_RATE']),应由工厂或上下文传入 - ❌ 不要:直接写日志或发 HTTP 请求——这些属于横切关注点,用装饰器或事件监听更合适
PHP 8.1+ 枚举配合策略接口更稳
用 enum 替代字符串标识策略类型,编译期就能捕获拼写错误,IDE 也能自动补全。
enum PaymentChannel: string {
case Alipay = 'alipay';
case Wechat = 'wechat';
}
<p>// 工厂方法签名可收紧
public static function get(PaymentChannel $channel): PaymentStrategy { ... }</p><p>// 调用时不再传魔法字符串
$strategy = PaymentStrategyFactory::get(PaymentChannel::Wechat);</p>
- 枚举值必须是
string或int,PHP 不支持对象枚举,所以不能把策略实例塞进枚举里 - 如果策略需要运行时动态加载(如插件机制),枚举仍可用,但工厂需额外映射:用
match($channel)关联到具体类名再new - 别为了用枚举而强行抽象——只有当策略类型固定且有限(≤10 种),枚举才真正提升可维护性
策略模式真正的难点不在写接口和实现,而在于划清边界:哪些逻辑属于策略内部,哪些该交给上下文、工厂或装饰器。越早明确这点,后续加新渠道、改计费规则时,就越不容易把 switch 嵌套成迷宫。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











