策略模式(strategy pattern)

news2024/11/15 9:07:38

背景

一、一个具体实现范例的逐步重构

  1. 补血道具(药品):a) 补血丹:补充200点生命值;b) 大还丹:补充300点生命值;c) 守护丹:补充500点生命值
  2. 将Fighter,F_Warrior,F_Mage 单独写在一个文件中。

Fighter.h

#pragma once
#ifndef __FIGHTER__
#define __FIGHTER__

enum ItemAddlife
{
	LF_BXD,   // 补血丹 
	LF_DHD,   //大还丹
	LF_SHD,   //守护丹
};

class Fighter
{
public:
	Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}
	virtual ~Fighter() {}

public:
	void UseItem(ItemAddlife type)
	{
		if (type == LF_BXD)       // 补血丹 
		{
			m_life += 200;
		}
		else if (type == LF_DHD)   //大还丹
		{
			m_life += 300;
		}
		else if (type == LF_SHD)   //守护丹
		{
			m_life += 500;
		}
	}
protected:
	int m_life;
	int m_magic;
	int m_attack;
};

class F_Warrior :public Fighter
{
public:
	F_Warrior(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};

class F_Mage :public Fighter
{
public:
	F_Mage(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};

#endif

test.cpp

#include <iostream>
#include <list>
#include <map>
#include <string>
#include "Fighter.h"

using namespace std;

int main()
{
	Fighter* prole_war = new F_Warrior(1000,0,200); //这里没有采用工厂模式,如果对象很多,可以考虑采用工厂模式创建对象
	prole_war->UseItem(LF_DHD);

	delete prole_war;

	return 0;
}
  1. 在主函数中,我们是通过 prole_war->UseItem(LF_DHD); 这种方式来进行回血操作。这样的操作存在一定的问题。
  2. 当业务逻辑越来越复杂的时候,ItemAddlife 中会存在很多其他的药品存在,void UseItem(ItemAddlift type) 这个函数肯定越来越复杂。因为需求和业务逻辑越来越复杂。这样不符合开闭原则。
  3. 因此需要 策略模式(strategy pattern) 来对代码进行重构。

策略模式定义:定义一系列算法(策略类),将每个算法封装起来,让他们可以互相替换。换句话说,策略模式通常把一系列算法封装到一系列具体策略类中来作为抽象策略类的子类, 然后根据实际需要使用这些子类。

在这里插入图片描述这个例子中的UML图,如上图所示。其实策略模式在实际的工程中特别常见,可以看一下下面这个实际工程中的UML图。
策略类中主要包含有三种角色。

  1. Context(环境类):该类中维持着一个对抽象策略类的指针或者引用。这里指Fighter类。
  2. Strategy(抽象策略类):定义所支持的算法的公共接口,是所有策略类的父类。这里指的是ItemStrategy类。
  3. ConcreteStrategy(具体策略类):抽象策略类的子类,实现抽象策略类中声明的接口。这里指的是 ItemStrategy_BXD,ItemStrategy_DHD,ItemStrategy_SHD。
  4. 策略类的优点:a) 以扩展的方式支持对未来的变化,符合开闭原则。b) 算法可以被复用。c) 策略模式可以看成是类继承的一种替代方案,通过为环境类对象指定不同的策略,就可以改变环境类对象的行为。
  5. 遇到大量不稳定的 if 条件分支 或者 switch分支,就要优先考虑是否可以通过策略模式来解决。策略模式是 if ,switch条件分支的杀手。
  6. 策略类的缺点:a) 导致引入许多新的策略类。b) 使用策略类时,调用者(main 主函数)必须熟知所有策略类的功能并根据实际需求自行决定使用那个策略类。

在这里插入图片描述
实际的大型工程中,充满了各种各样的设计模式。

Fighter.h

#pragma once
#ifndef __FIGHTER__
#define __FIGHTER__

class ItemStrategy;

/*enum ItemAddlife
{
	LF_BXD,   // 补血丹 
	LF_DHD,   //大还丹
	LF_SHD,   //守护丹
};*/

class Fighter
{
public:
	Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}
	virtual ~Fighter() {}

public:
	/*
	void UseItem(ItemAddlife type)
	{
		if (type == LF_BXD)       // 补血丹 
		{
			m_life += 200;
		}
		else if (type == LF_DHD)   //大还丹
		{
			m_life += 300;
		}
		else if (type == LF_SHD)   //守护丹
		{
			m_life += 500;
		}
	}*/
public:
	void SetItemStrategy(ItemStrategy* strategy);
	void UseItem();
	int GetLife();
	void SetLife(int life);
protected:
	int m_life;
	int m_magic;
	int m_attack;
	ItemStrategy* itemstrategy = nullptr;
};

class F_Warrior :public Fighter
{
public:
	F_Warrior(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};

class F_Mage :public Fighter
{
public:
	F_Mage(int life, int magic, int attack) : Fighter(life, magic, attack) {}
};

#endif

Fighter.cpp

#include "Fighter.h"
#include "ItemStrategy.h"
#include <iostream>

using namespace std;

void Fighter::SetItemStrategy(ItemStrategy* strategy)
{
	itemstrategy = strategy;
}

//使用道具
void Fighter::UseItem()
{
	itemstrategy->UseItem(this);
}

int Fighter::GetLife()
{
	return m_life;
}

void Fighter::SetLife(int life)
{
	m_life = life;
}

ItemStrategy.h

#pragma once
#ifndef __ITEMSTRATEGY__
#define __ITEMSTRATEGY__

#include "Fighter.h"

class Fighter;

class ItemStrategy
{
public:
	virtual void UseItem(Fighter* obj) = 0;
	virtual ~ItemStrategy() {}
};

class ItemStrategy_BXD :public ItemStrategy
{
public:
	virtual void UseItem(Fighter* obj)
	{
		obj->SetLife(obj->GetLife()+200);
	}
};

class ItemStrategy_DHD :public ItemStrategy
{
public:
	virtual void UseItem(Fighter* obj)
	{
		obj->SetLife(obj->GetLife()+300);
	}
};

class ItemStrategy_SHD :public ItemStrategy
{
public:
	virtual void UseItem(Fighter* obj)
	{
		obj->SetLife(obj->GetLife()+500);
	}
};

#endif __ITEMSTRATEGY__

test.cpp

#include <iostream>
#include <list>
#include <map>
#include <string>
#include "ItemStrategy.h"

using namespace std;

int main()
{
	Fighter* prole_war = new F_Warrior(1000,0,200);

	cout << prole_war->GetLife() << endl;
	
	//吃一颗大还丹
	ItemStrategy* strategyDHD = new ItemStrategy_DHD(); //创建大还丹策略
	prole_war->SetItemStrategy(strategyDHD); //主角设置大还丹策略,准备吃大还丹
	prole_war->UseItem(); //主角吃大还丹

	cout << prole_war->GetLife() << endl;

	//吃一颗补血丹
	ItemStrategy* strategyBXD = new ItemStrategy_BXD(); //创建补血丹策略
	prole_war->SetItemStrategy(strategyBXD);  //主角设置补血丹策略,准备吃补血丹
	prole_war->UseItem(); //主角吃补血丹
	
	cout << prole_war->GetLife() << endl;


	delete prole_war;
	delete strategyDHD;
	delete strategyBXD;

	return 0;
}

依赖倒置原则

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/129639.html

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

相关文章

一步一步学爬虫(4)数据存储之文本存储

一步一步学爬虫&#xff08;4&#xff09;数据存储之文本存储4.1 TXT纯文本文件存储4.1.1 本节目标4.1.2 基本实例4.1.3 打开方式4.1.4 简化写法4.1 TXT纯文本文件存储 将数据保存到 TXT 文本的操作非常简单&#xff0c;而且 TXT 文本几乎兼容任何平台&#xff0c;但是这有个缺…

Spark环境搭建(Hadoop YARN模式)

前言 按照前面环境部署中所学习的&#xff0c;如果我们想要一个稳定的生产Spark环境&#xff0c;那么最优的选择就是构建&#xff1a;HA StandAlone集 群。 不过在企业中&#xff0c; 服务器的资源总是紧张的&#xff0c;许多企业不管做什么业务&#xff0c;都基本上会有Hadoo…

AD软件绘制不规则焊盘的器件封装

网上有很多关于AD软件绘制不规则焊盘的帖子&#xff0c;搜了一些帖子看了一下&#xff0c;感觉不太对。严格意义上AD软件是不能绘制不规则的焊盘的&#xff0c;至少目前用的AD软件不支持。为什么这么说呢&#xff1f; 我提一个需求&#xff1a;假如我在PCB文件中需要随意的添加…

企业选择SOP作业指导书系统的目的和意义

SOP是将作业指导流程予以说明规范&#xff0c;让作业人员有一个标准的作业准则&#xff0c;以达到作业的标准一致性。也是一种管理模式&#xff0c;通过对过程的标准化操作&#xff0c;减少和预防差错和不良后果的发生。通过动作的解析、比较、分析&#xff0c;循环作业分析等输…

MIT6.830-2022-lab4实验思路详细讲解

目录前言一、实验概览Exercise 1:Granting LocksExercise 2:Lock LifetimeExercise 3:Implementing NO STEALExercise 4:TransactionsExercise 5&#xff1a;Deadlocks and Aborts总结 && Debug记录前言 到lab4事务&#xff0c;这一块应该是数据库中概念比较多&#x…

2022/12/30总结

今日学习了二叉树有关知识。 二叉树 二叉树通俗来讲就是一个有俩个指针的链表。他们大多长这个样子&#xff1a; 这里还有俩个概念了&#xff0c;二叉树分为完全二叉树和满二叉树 上面所说的是满二叉树&#xff0c;顾名思义就是每个父节点都相应的有俩个指针&#xff0c;通常…

基于springboot+Vue的宿舍管理系统前后端分离(程序+详细文档+数据库)

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

前端devops——利用gitlab实现CI/CD自动化部署

目录 前言 一、前期准备 1、开启虚拟服务 2、下载并安装docker 二、开始部署 1、安装gitlab 2、修改默认账号登录密码 3、修改项目clone地址 三、Gitlab CI/CD 1、安装并运行gitlab-runner 2、执行runner 3、将项目注册到gitlab-runner 1、获取token 2、执行注册 …

ETL数据清洗

大多数据仓库的数据架构可以概括为&#xff1a; 数据源-->ODS(操作型数据存储)-->DW-->DM(data mart) ETL贯穿其各个环节。 ​一、数据抽取&#xff1a; 可以理解为是把源数据的数据抽取到ODS或者DW中。 1. 源数据类型&#xff1a; 关系型数据库&#xff0c;如Or…

多模态串讲(上)

多模态的学习在最近几年异常火爆&#xff0c;除了普通的多模态学习&#xff0c;比如视觉问答&#xff0c;图文检索等,其实之前讲的所有这种Language Guided Detection&#xff0c;或者Language Guided Segmentation&#xff0c;这些任务都是多模态的&#xff0c;还有最近火的文…

SpringCloud之负载均衡Ribbon

1.Ribbon是什么&#xff1f; •Ribbon是 Netflix 提供的一个基于HTTP和TCP的客户端负载均衡工具。 •Ribbon主要有两个功能&#xff1a; 1.简化远程调用 2.提供客户端的软件负载均衡算法 Ribbon客户端组件提供一系列完善的配置项&#xff0c;如连接超时&#xff0c;重试等…

ERA5数据不同下载方法

ERA5数据不同下载方法1 ERA5简介2 ERA5下载的三种方法2.1 方法1&#xff1a;GEE下载2.2 方法2&#xff1a;官方网站下载2.3 方法3&#xff1a;通过Python脚本下载&#xff08;以Linux系统为例&#xff09;总结参考1 ERA5简介 ERA5是ECMWF对全球气候的第五代大气再分析。再分析…

ArcGIS基础实验操作100例--实验17按条件计算属性字段值

本实验专栏来自于汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 基础编辑篇--实验17 按条件计算属性字段值 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff0…

工业远程I/O模块CANopen I/O模块 安装接线说明

1&#xff09;外观尺寸 DIN35mm Rail标准导轨支架外观与尺寸&#xff1a; 2&#xff09;面板说明 指示灯说明运行指示灯 绿色预留预留错误指示灯 红色CANopen地址设置开关&#xff0c;CANopen 通讯速率设置开关&#xff0c;当 0表示通信速率为10Kbps 4表示通信速率为250Kbps 1表…

Linux | 内存 | 由内存页不足(page allocation failure)引起程序杀死(OOM Killer)

本文对由于 page allocation failure 而引起 Out of Memory Killer 的背景及工作原理进行不完全总结。 更新&#xff1a;2022 / 12 / 30 文章目录触发条件__alloc_pages_slowpath()__vmalloc_area_node()__vmalloc_node_range工作原理结合实例1.2.GFP_ATOMIC 和 __GFP_COMP&am…

阿里云弹性预测 AHPA:助力厨芯科技降本增效

作者&#xff1a;李鹏&#xff08;元毅&#xff09; “使用阿里云弹性预测 AHPA&#xff0c;降低了 K8s 容器成本&#xff0c;同时减轻了运维工作量&#xff0c;加速了业务容器化的进程。”—— 朱晏(厨芯科技VP) 背景 厨芯科技&#xff0c;是全球领先的餐饮设备和服务提供商…

TCP 的报头结构 和 三次握手---详解(看完必会)

TCP 的三次握手&#xff1a; 在搞懂三次握手前&#xff0c;必须要搞明白TCP报头的结构内容 TCP报头结构&#xff1a; 源端口号 : 源计算机上的应用程序的端口号&#xff1b;目的端口号 : 目标计算机的应用程序端口号&#xff1b;序列号&#xff1a;客户端给服务端发送数据时…

React 配置文件(二) 配置环境变量

开发环境一般分为: UAT(测试环境) PRE(预上线环境) PROD(生产环境) 所以本地开发分别搭建相对应环境 2.安装 dotenv 3.在package.json文件中配置环境 "scripts": { "start": "react-app-rewired start", "uat": "dotenv…

【Linux】文件系统与inode

一、文件系统 理解文件系统前首先我们要来先了解一下磁盘结构。 接下来我们看看以水平、垂直角度来看看磁盘结构&#xff0c;并将其区域进行划分。 磁盘的垂直分布 (此图最上面的一面和最下面的一面无磁头&#xff0c;则不存储数据)&#xff1a; 磁头数&#xff1a;磁头就是在…

LeetCode303.区域和检索 - 数组不可变

LeetCode刷题记录 文章目录&#x1f4dc;题目描述&#x1f4a1;解题思路⌨C代码&#x1f4dc;题目描述 给定一个整数数组 nums&#xff0c;处理以下类型的多个查询: 计算索引 left 和 right &#xff08;包含 left 和 right&#xff09;之间的 nums 元素的 和 &#xff0c;其中…