2.策略模式(Strategy)

定义

定义一系列算法,把它们一个个封装起来,并且使他们可互相替换(变化)。该模式使算法可独立于 使用它的客户程序(稳定)而变化(拓展,子类化)。

动机(Motivation)

在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使用的算法也是一个性能负担。

例如在电子商务中的一个税种订单计算。我们首先想到的实现方法是先定义一个枚举类型,定义出中国美国德国的税法计算。

在涉及到税法计算中 CalculateTax 我们通过这个枚举类型进行相应的计算。

cpp 复制代码
enum TaxBase {
	CN_Tax, // 中国
	US_Tax, // 美国
	DE_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***********
        } 
        //....
     }
    
};

比方说 未来出现变化了,需要支持 法国的税法。

首先需要在枚举类型 TaxBase 中增加一个枚举类型 FR_Tax //法国,

然后再 CalculateTax 中继续添加 if (tax == FR_Tax)

完整代码如下:

cpp 复制代码
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){  //更改
			//...
		}

        //....
     }
    
};

很显然上面的代码违反了 开放封闭原则,即对拓展开发,对更改封闭。就是类模块尽可能用拓展的方式来支持未来的变化,而不是直接修改源代码。

下面开始改进

  1. 首先不使用枚举类型 ,而是定义一个税法策略的基类
    它有一个计算的方法 Calculate,是纯虚函数
cpp 复制代码
class TaxStrategy{
public:
    virtual double Calculate(const Context& context)=0;
    virtual ~TaxStrategy(){}
};

2.对于各个国家的税法,我们进行单独的计算,其实就是将

cpp 复制代码
  if (tax == CN_Tax){
            //CN***********
        }

类似这部分 CN ... US ... 等这类算法部分 放到类的纯虚函数中,变成 TaxStrategy 的子类。

cpp 复制代码
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){
        //***********
    }
};
  1. 创建多态指针 TaxStrategy * strategy ,这里使用的是外界传入的,这里可能返回的是 DETax 或 USTax 或 CNTax 等的对象,至于是哪一个,由工厂模式确定,在 SalesOrder(StrategyFactory* strategyFactory) 中的函数参数传入。如果传入是 USTax 那么就是美国。
    首先构建算法上下文 Context context(); ,然后直接调用
    strategy->Calculate(context); 这就是一个多态调用,可能会调用 中国税法,也可能调用美国,或其它。
  2. 从时间轴看好处。加入增加一个法国的税计算,那么只需要增加
    class FRTax : public TaxStrategy{
    public:
    virtual double Calculate(const Context& context){
    //***********
    }
    };
    然后其他的代码就不需要改变。

完整代码如下:

cpp 复制代码
class SalesOrder{
private:
	// 这里期望这个指针指向不同的strategy的子类
    TaxStrategy* strategy;

public:
    SalesOrder(StrategyFactory* strategyFactory){
    	// 实例化一个对象
        this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder(){
        delete this->strategy;
    }

    public double CalculateTax(){
        //...
        Context context();
        
        double val = 
            strategy->Calculate(context); //多态调用
        //...
    }
    
};
cpp 复制代码
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:
	// 这里期望这个指针指向不同的strategy的子类
    TaxStrategy* strategy;

public:
    SalesOrder(StrategyFactory* strategyFactory){
    	// 实例化一个对象
        this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder(){
        delete this->strategy;
    }

    public double CalculateTax(){
        //...
        Context context();
        
        double val = 
            strategy->Calculate(context); //多态调用
        //...
    }
    
};

总结

Strategy 及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。

Strategy 模式提供了用条件判断语句之外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要 Strategy 模式。

如果 Strategy 对象没有实例变量,那么各个上传下文可以共享同一个 Strategy 对象,从而节省对象开销。

相关推荐
skywalk81633 小时前
esxi8 虚拟机中怎么安装mac os(纯AI回答,未实践)
策略模式·esxi
廖圣平10 小时前
从零开始,福袋直播间脚本研究【八】《策略模式》
开发语言·python·bash·策略模式
爱学习 爱分享4 天前
简单工厂模式和策略模式的区别
简单工厂模式·策略模式
xcntime7 天前
Python中print函数如何实现不换行输出?
策略模式
青春易逝丶8 天前
策略模式
java·开发语言·策略模式
sg_knight8 天前
设计模式实战:策略模式(Strategy)
java·开发语言·python·设计模式·重构·架构·策略模式
liangshanbo12158 天前
[特殊字符] macOS 上的 zoxide:智能目录跳转终极指南
macos·策略模式
Rabbit_QL10 天前
【Warp+Claude】任务完成自动通知(macOS + Warp 版)
macos·策略模式
Sahadev_11 天前
macOS 解决 AirDrop 传输应用“已损坏“问题,以sublime为例
macos·策略模式·sublime text
筱璦12 天前
期货软件开发「启动加载页 / 初始化窗口」
前端·c#·策略模式·期货