当前位置: 首页 > article >正文

2.策略模式(Strategy)

定义

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

动机(Motivation)

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

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

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)
完整代码如下:

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,是纯虚函数
class TaxStrategy{
public:
    virtual double Calculate(const Context& context)=0;
    virtual ~TaxStrategy(){}
};

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

  if (tax == CN_Tax){
            //CN***********
        }

类似这部分 CN … US … 等这类算法部分 放到类的纯虚函数中,变成 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){
        //***********
    }
};
  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){
    //***********
    }
    };
    然后其他的代码就不需要改变。

完整代码如下:


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); //多态调用
        //...
    }
    
};

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 对象,从而节省对象开销。


http://www.kler.cn/a/523748.html

相关文章:

  • 解锁豆瓣高清海报:深度爬虫与requests进阶之路
  • 【视频+图文详解】HTML基础3-html常用标签
  • deepseek R1的确不错,特别是深度思考模式
  • Rust:Rhai脚本编程示例
  • 2501,20个窗口常用操作
  • selenium自动化测试框架——面试题整理
  • 如何使用Git进行版本控制?
  • 单细胞分析基础-第一节 数据质控、降维聚类
  • NLP自然语言处理通识
  • 前端25.1.26学习记录
  • IDM-VTON本地部署教程:双重编码 + 文字提示,解锁真实野外试穿
  • 【Elasticsearch】 索引模板 ignore_missing_component_templates
  • 【自学嵌入式(6)天气时钟:软硬件准备、串口模块开发】
  • 一文大白话讲清楚webpack进阶——5——dev-server原理及其作用
  • 【信息系统项目管理师-选择真题】2010上半年综合知识答案和详解
  • java求职学习day15
  • dokploy 如何部署 nuxt 项目?(进来少踩坑)
  • 【uniapp】uniapp使用java线程池
  • 1.1 画质算法的主要任务
  • AI软件栈:LLVM分析(二)
  • TL494方案开关电源方案
  • 更新文章分类
  • 在sortablejs的拖拽排序情况下阻止input拖拽事件
  • 解决报错“The layer xxx has never been called and thus has no defined input shape”
  • Vue 3 中的 TypeScript:接口、自定义类型与泛型
  • Android View 的事件分发机制解析