1. 动机
- 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使用的算法也是一个性能负担 (资源浪费)。
- 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
2. 模式定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
- 用拓展的方式,应对未来的算法层面的需求变化
实例:
跨国电商 - 税务计算
业务变化 - 时间轴 - 增加其他国家的税务计算方式
//结构化软件设计 - 面向过程
enum TaxBase{
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //业务更改
};
class SalesOrder{
TaxBase tax;
public:
double CalculateTax(){
//...
if(tax == CN_Tax){
//CN********
}else if(tax == US_Tax){
//US********
}else if(tax == DE_Tax){
//DE********
}else if(tax == FR_Tax){ //业务更改
//FR********
}
/*
业务更改 违反了开发封闭原则 OCP
- 对扩展开放, 对更改封闭
- 需求变更 => 增加代码 而不是修改代码
- 直接修改源代码, 会增加不必要的成本(重新编译 测试 部署)
*/
//...
}
}
- 不要以静态地眼光去看待软件结构的设计, 而是拥有动态思维
- 即, 时间轴的概念: 过去——现在——将来 [问题的暴露]
- 业务代码 软件架构怎么变化?怎么应对变化?
//面向对象软件设计
class TaxStrategy{
public:
virtual double Calculate(const Context& context) = 0;
virtual ~TaxStrategy(){}//虚-析构函数
}
class CNTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//*********
}
}
class USTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//*********
}
}
class DETax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//*********
}
}
//拓展 - 遵循开发封闭原则
/**************业务更改*******************/
class FRTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//*********
}
}
class SalesOrder{
private:
TaxStrategy* strategy;//多态指针
public:
SalesOrder(StrategyFactory* strategyFactory){
//工厂模式 - 创建对象
this->strategy = strategyFactory->NewStrategy();
}
~SalesOrder(){
delete this->strategy;
}
public double CalculateTax(){
//...
Context context();
double val =
strategy->Calculate(context);//多态调用
//...
}
};
- 复用性指的是 编译单位,二进制层面的复用,
而不是源代码层面的复用(Ctrl + C/V) - 性能负担:CPU高级缓存 - 内存 - 硬盘(虚拟内存)
3. 结构
- 稳定的部分
Context
Strategy
- 变化的部分
ConcreteStrategyA
ConcreteStrategyB
ConcreteStrategyC
4. 要点总结
-
Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
-
Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
switch-case
或着if-else-if
这是结构化软件设计时代,分而治之的思维
不同的情况,不同的应对策略,
但在时间轴上来看,不可能预测未来的情况,
也就是列举所有的情况,来分而治之的进行解决一旦情况变化就需要修改源代码,违背开发封闭原则
所以,看到 条件判断语句 思考是否可用 Strategy 模式替代
当然,也不是所有的条件判断语句都用 Strategy 模式替代,
条件判断语句绝对稳定、不会发生变化的情况下,则不需要替代。
比如,确定的公认事实,一周有七天,可确定的情况
“看到
if-else-if
闻到坏代码的味道” -
如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。