策略模式(Strategy Pattern)
定义
定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
设计的原则和思想
- 解耦策略的定义、创建和使用这三部分。
- 不变部分是算法本身,变化的部分是算法的调用。
- 核心思想是动态确定算法(变化)。
一句话概括设计模式
相同的接口,相同的方法,不同的策略。
结构中包含的角色
- Strategy(抽象策略类)
- ConcreteStrategy(具体策略类)
- Context(环境类)
最小可表达代码
// 抽象策略类 | |
abstract class AbstractStrategy | |
{ | |
public abstract function algorithm(); | |
} | |
// 具体策略类 | |
class ConcreteStrategy extends AbstractStrategy | |
{ | |
public function algorithm() | |
{ | |
echo "具体算法"; | |
} | |
} | |
// 环境类 | |
class Context | |
{ | |
private $strategy; | |
public function __construct(AbstractStrategy $strategy) | |
{ | |
$this->strategy = $strategy; | |
} | |
public function algorithm() | |
{ | |
$this->strategy->algorithm(); | |
} | |
} | |
$context = new Context(new ConcreteStrategy()); | |
$context->algorithm(); |
优点
- 可以自由切换算法。
- 避免了多重条件判断。
- 算法单独提取到策略类中,提供了一种算法的复用机制。
缺点
- 因为需要选择合适的策略,客户端需要知道所有的策略算法。
- 任何的变化都将导致系统要增加一个新的具体策略类。
- 客户端每次只能使用一个策略类。
何时使用
- 需要动态地切换不同算法。
- 一个对象有很多的行为,将这些行为转移到相应的具体策略类可以避免维护多重条件语句。
实际应用场景
- 出行是骑自行车, 坐汽车还是走路。
- 一条鱼可以清蒸、红烧、炭烤等。
- 处理订单是同步还是异步。
- 日志记录到数据库,文件还是缓存。
注意
- 算法很少发生改变就没有必要使用这个模式了,使用会让程序更复杂。
- 如果策略多余4个,那么需要使用多个设计模式混合了来解决策略类膨胀的问题了。
策略的定义、创建和使用
策略的定义
一个策略接口和一组实现这个接口的策略类。
策略的创建
为了封装创建逻辑,可以把根据 type 创建策略的逻辑抽离出来,放到工厂类中。
策略的使用
运行时动态确定使用哪种策略,这也是策略模式最典型的应用场景。