设计模式全解析:掌握策略模式的精髓
模式定义
策略模式(Strategy Pattern)是行为型设计模式,定义算法家族并封装每个算法,使它们可以互相替换。该模式让算法的变化独立于使用算法的客户端。
核心思想
算法独立封装:每个算法单独封装成类
运行时动态切换:客户端可自由选择具体策略
消除条件分支:避免大量if-else判断逻辑
适用场景
多种算法变体需要灵活切换
需要消除复杂的条件语句
存在相似算法需要统一管理
需要隔离算法实现与使用
模式结构
Strategy:策略接口
ConcreteStrategy:具体策略实现
Context:策略容器和执行环境
PHP实现示例:电商促销策略
<?php
// 促销策略接口
interface PromotionStrategy {
public function apply(float $price): float;
}
// 具体策略:无优惠
class NoDiscount implements PromotionStrategy {
public function apply(float $price): float {
return $price;
}
}
// 具体策略:满减
class FullReduction implements PromotionStrategy {
public function apply(float $price): float {
if ($price >= 200) {
return $price - 50;
}
return $price;
}
}
// 具体策略:百分比折扣
class PercentageDiscount implements PromotionStrategy {
public function apply(float $price): float {
return $price * 0.8;
}
}
// 策略上下文
class PromotionContext {
private $strategy;
public function __construct(PromotionStrategy $strategy) {
$this->strategy = $strategy;
}
public function setStrategy(PromotionStrategy $strategy) {
$this->strategy = $strategy;
}
public function execute(float $price): float {
return $this->strategy->apply($price);
}
}
// 客户端使用
$context = new PromotionContext(new NoDiscount());
$prices = [150, 250, 300];
foreach ($prices as $price) {
// 动态切换策略
if ($price > 200) {
$context->setStrategy(new PercentageDiscount());
} elseif ($price >= 200) {
$context->setStrategy(new FullReduction());
}
$final = $context->execute($price);
echo "原价:{$price} => 实付:{$final}\n";
}
/* 输出:
原价:150 => 实付:150
原价:250 => 实付:200
原价:300 => 实付:240
*/
Go实现示例:文件压缩策略
package main
import "fmt"
// 压缩策略接口
type CompressionStrategy interface {
Compress(file string) string
}
// ZIP压缩策略
type ZipStrategy struct{}
func (z *ZipStrategy) Compress(file string) string {
return fmt.Sprintf("%s.zip", file)
}
// RAR压缩策略
type RarStrategy struct{}
func (r *RarStrategy) Compress(file string) string {
return fmt.Sprintf("%s.rar", file)
}
// 7z压缩策略
type SevenZStrategy struct{}
func (s *SevenZStrategy) Compress(file string) string {
return fmt.Sprintf("%s.7z", file)
}
// 压缩上下文
type Compressor struct {
strategy CompressionStrategy
}
func (c *Compressor) SetStrategy(s CompressionStrategy) {
c.strategy = s
}
func (c *Compressor) Execute(file string) string {
if c.strategy == nil {
return file
}
return c.strategy.Compress(file)
}
func main() {
files := []string{"report.doc", "data.csv", "images"}
compressor := &Compressor{}
// 按文件类型选择策略
for _, file := range files {
switch {
case len(file) > 4 && file[len(file)-4:] == ".csv":
compressor.SetStrategy(&ZipStrategy{})
case len(file) > 4 && file[len(file)-4:] == ".doc":
compressor.SetStrategy(&RarStrategy{})
default:
compressor.SetStrategy(&SevenZStrategy{})
}
result := compressor.Execute(file)
fmt.Printf("原始文件: %-10s → 压缩结果: %s\n", file, result)
}
}
/* 输出:
原始文件: report.doc → 压缩结果: report.doc.rar
原始文件: data.csv → 压缩结果: data.csv.zip
原始文件: images → 压缩结果: images.7z
*/
模式优缺点
✅ 优点:
符合开闭原则,易于扩展新策略
消除复杂的条件判断语句
算法实现与使用解耦
便于单元测试
❌ 缺点:
策略类数量可能爆炸式增长
客户端必须了解策略差异
增加对象间通信开销
不同语言实现差异
模式变体与演进
Lambda策略
使用匿名函数实现轻量级策略(PHP 5.3+/Go 1.18+)策略工厂
结合工厂模式管理策略创建组合策略
多个策略组合使用(如折扣+满减)状态模式关联
策略切换与状态变更联动配置化策略
从配置文件动态加载策略
最佳实践建议
策略粒度控制
保持策略的单一职责,避免过度细分策略无状态化
尽量设计无状态的策略类,方便复用默认策略设置
提供合理的默认策略避免空策略策略文档规范
为每个策略编写清晰的文档说明性能优化
对高频使用策略考虑对象池技术
策略模式VS状态模式
总结
策略模式通过将算法封装为独立对象,实现了算法定义与算法使用的彻底解耦。该模式在以下场景表现尤为出色:
需要灵活切换多种算法变体
存在大量条件判断语句
算法需要独立演进和复用
系统需要动态选择算法策略
PHP与Go的实现差异体现了不同语言的哲学:
PHP通过接口约束和依赖注入确保策略合规
Go利用隐式接口和组合实现更灵活的扩展
实际应用时需要注意:
合理控制策略粒度
避免策略类膨胀
结合其他模式(如工厂模式)优化创建
做好策略的文档管理和版本控制
掌握策略模式的精髓在于理解"委托代替继承"的思想,这种将变化部分抽象封装的设计理念,是构建高扩展性系统的关键所在。