2.策略模式(Strategy)

news2025/2/5 8:07:12

定义

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

动机(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.coloradmin.cn/o/2292199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手写MVVM框架-构建虚拟dom树

MVVM的核心之一就是虚拟dom树,我们这一章节就先构建一个虚拟dom树 首先我们需要创建一个VNode的类 // 当前类的位置是src/vnode/index.js export default class VNode{constructor(tag, // 标签名称(英文大写)ele, // 对应真实节点children,…

【Blazor学习笔记】.NET Blazor学习笔记

我是大标题 我学习Blazor的顺序是基于Blazor University,然后实际内容不完全基于它,因为它的例子还是基于.NET Core 3.1做的,距离现在很遥远了。 截至本文撰写的时间,2025年,最新的.NET是.NET9了都,可能1…

C++11中的bind

官方文档对于bind接口的概述解释:Bind function arguments 在C11中,std::bind 是一个非常有用的工具,用于将函数、成员函数或函数对象与特定的参数绑定在一起,生成一个新的可调用对象。std::bind 可以用于部分应用函数参数、改变…

鼠标拖尾特效

文章目录 鼠标拖尾特效一、引言二、实现原理1、监听鼠标移动事件2、生成拖尾元素3、控制元素生命周期 三、代码实现四、使用示例五、总结 鼠标拖尾特效 一、引言 鼠标拖尾特效是一种非常酷炫的前端交互效果,能够为网页增添独特的视觉体验。它通常通过JavaScript和C…

金山打字游戏2010绿色版,Win7-11可用DxWnd完美运行

金山打字游戏2010绿色版,Win7-11可用DxWnd完美运行 链接:https://pan.xunlei.com/s/VOIAYCzmkbDfdASGJa_uLjquA1?pwd67vw# 进入游戏后,如果输入不了英文字母(很可能是中文输入状态),就按一下“Shift”键…

爬虫学习笔记之Robots协议相关整理

定义 Robots协议也称作爬虫协议、机器人协议,全名为网络爬虫排除标准,用来告诉爬虫和搜索引擎哪些页面可以爬取、哪些不可以。它通常是一个叫做robots.txt的文本文件,一般放在网站的根目录下。 robots.txt文件的样例 对有所爬虫均生效&#…

(10) 如何获取 linux 系统上的 TCP 、 UDP 套接字的收发缓存的默认大小,以及代码范例

(1) 先介绍下后面的代码里要用到的基础函数: 以及: (2) 接着给出现代版的 读写 socket 参数的系统函数 : 以及: (3) 给出 一言的 范例代码,获取…

【玩转 Postman 接口测试与开发2_016】第13章:在 Postman 中实现契约测试(Contract Testing)与 API 接口验证(上)

《API Testing and Development with Postman》最新第二版封面 文章目录 第十三章 契约测试与 API 接口验证1 契约测试的概念2 契约测试的工作原理3 契约测试的分类4 DeepSeek 给出的契约测试相关背景5 契约测试在 Postman 中的创建方法6 API 实例的基本用法7 API 实例的类型实…

day8-面向对象

目录 面向对象1、面向对象介绍2、类和对象类和对象类的几个补充注意事项 3、封装 面向对象 1、面向对象介绍 ⭐️面向对象介绍: 面向:拿、找对象:能干活的东西面向对象编程:拿东西过来做对应的事情 面向对象编程的例子&#x…

【Java】位图 布隆过滤器

位图 初识位图 位图, 实际上就是将二进制位作为哈希表的一个个哈希桶的数据结构, 由于二进制位只能表示 0 和 1, 因此通常用于表示数据是否存在. 如下图所示, 这个位图就用于标识 0 ~ 14 中有什么数字存在 可以看到, 我们这里相当于是把下标作为了 key-value 的一员. 但是这…

【自然语言处理(NLP)】生成词向量:GloVe(Global Vectors for Word Representation)原理及应用

文章目录 介绍GloVe 介绍核心思想共现矩阵1. 共现矩阵的定义2. 共现概率矩阵的定义3. 共现概率矩阵的意义4. 共现概率矩阵的构建步骤5. 共现概率矩阵的应用6. 示例7. 优缺点优点缺点 **总结** 目标函数训练过程使用预训练的GloVe词向量 优点应用总结 个人主页:道友老…

如何获取sql数据中时间的月份、年份(类型为date)

可用自带的函数month来实现 如: 创建表及插入数据: create table test (id int,begindate datetime) insert into test values (1,2015-01-01) insert into test values (2,2015-02-01) 执行sql语句,获取月份: select MONTH(begindate)…

NSSCTF Pwn [NISACTF 2022]ezpie 题解

下载 exeinfo checksec 32位 NX PIE开启 查看main函数: 后门函数: 发现已经给出了main函数地址 因为开启了PIE 所以IDA中 后门函数减去main函数的后三位就是偏移 给出的函数加上这个数就是后门函数地址 exp: from pwn import *p remote(…

【01】共识机制

BTF共识 拜占庭将军问题 拜占庭将军问题是一个共识问题 起源 Leslie Lamport在论文《The Byzantine Generals Problem》提出拜占庭将军问题。 核心描述 军中可能有叛徒,却要保证进攻一致,由此引申到计算领域,发展成了一种容错理论。随着…

文字加持:让 OpenCV 轻松在图像中插上文字

前言 在很多图像处理任务中,我们不仅需要提取图像信息,还希望在图像上加上一些文字,或是标注,或是动态展示。正如在一幅画上添加一个标语,或者在一个视频上加上动态字幕,cv2.putText 就是这个“文字魔术师”,它能让我们的图像从“沉默寡言”变得生动有趣。 今天,我们…

【R语言】环境空间

一、环境空间的特点 环境空间是一种特殊类型的变量,它可以像其它变量一样被分配和操作,还可以以参数的形式传递给函数。 R语言中环境空间具有如下3个特点: 1、对象名称唯一性 此特点指的是在不同的环境空间中可以有同名的变量出现&#x…

惰性函数【Ⅱ】《事件绑定的自我修养:从青铜到王者的进化之路》

【Ⅱ】《事件绑定的自我修养:从青铜到王者的进化之路》 1. 代码功能大白话(给室友讲明白版) // 青铜写法:每次都要问浏览器"你行不行?" function addEvent青铜版(element, type, handler) {if (window.add…

Vue3的el-table-column下拉输入实时查询API数据选择的实现方法

由于本人对el-table-column有下拉输入选择的要求&#xff0c;根据网上搜索的资料及本人优化&#xff0c;推出我比较满意的方法&#xff0c;供各位读者参考使用。 效果图 el-table-column写法 <el-table-columnlabel"货品编号"align"center"prop"…

[mmdetection]fast-rcnn模型训练自己的数据集的详细教程

本篇博客是由本人亲自调试成功后的学习笔记。使用了mmdetection项目包进行fast-rcnn模型的训练&#xff0c;数据集是自制图像数据。废话不多说&#xff0c;下面进入训练步骤教程。 注&#xff1a;本人使用linux服务器进行展示&#xff0c;Windows环境大差不差。另外&#xff0…

RFID涉密载体管控系统|支持国产化、自主研发

涉密载体管理系统DW-S402是一套成熟系统&#xff0c;是用于对各种涉密载体进行有效管理&#xff0c;实现对载体的智能化、规范化、标准化管理&#xff0c;广泛应用于保密、机要单位以及企事业单位等有载体保管需求的行业。 用户为对文件资料、存储介质等管理严格的单位&#x…