C++11 设计模式7 策略模式 ,Strategy

策略模式的概念:

策略模式(Strategy Pattern)是 C++ 中常用的一种行为设计模式,它能在运行时改变对象的行为。在策略模式中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为模式。

在策略模式中,需要创建表示各种策略的对象和一个行为随着策略对象改变而改变的 Context 对象。策略对象更改 Context 对象的执行算法。

在策略模式中,通常包括以下几个角色:

(1)策略接口(Strategy Interface): 定义了一个策略的公共接口,所有具体的策略类都需要实现这个接口。这个接口声明了策略对象将执行的操作。

(2)具体策略类(Concrete Strategy Classes): 实现了策略接口,提供了具体的算法或行为。每个具体策略类都封装了实现特定行为或算法的代码。

(3)上下文(Context): 维护一个指向策略对象的引用,并定义一个接口来让策略对象执行其算法。上下文类并不知道具体的策略类,它只知道策略接口。这样,上下文可以将请求转发给当前关联的策略对象来执行。

2 策略模式的实现步骤
在 C++ 实现策略模式的实现步骤如下:

(1)定义策略接口:
首先,需要定义一个策略接口。这个接口通常是一个纯虚类,声明了一组公共的、需要由具体策略类来实现的方法。这些方法是策略对象将执行的行为的抽象描述。

(2)实现具体策略类:
接下来,创建实现策略接口的具体策略类。每个具体策略类都包含了实现特定算法或行为的代码。这些类继承了策略接口,并实现了接口中声明的所有方法。

(3)创建上下文类:
上下文类负责维护对策略对象的引用,并定义了一个接口,以便客户端代码可以通过这个接口来执行策略对象的方法。上下文类通常包含一个指向策略接口的指针或引用,并通过这个指针或引用来调用策略方法。上下文类本身并不关心具体使用了哪个策略,它只关心策略接口。

(4)在上下文中设置策略对象:
在客户端代码中,创建具体策略类的对象,并将其传递给上下文对象。上下文对象使用这个策略对象来执行相应的算法或行为。客户端代码可以通过调用上下文类的方法来间接调用策略对象的方法。

(5)执行策略:
客户端代码通过调用上下文类的执行方法(例如 executeStrategy()),来触发策略的执行。上下文类将调用当前设置的策略对象的方法,实现相应的算法或行为。

(6)更改策略:
如果需要改变行为,客户端代码可以创建另一个策略对象,并将其设置为上下文对象的新策略。这样,上下文对象在执行策略时会使用新的算法或行为。

通过这些步骤,策略模式允许在运行时动态地改变对象的行为,提高了代码的灵活性和可维护性。它通过将算法和行为封装在独立的策略类中,实现了算法与使用算法的客户端代码之间的解耦。这样,客户端代码只需关注于如何使用策略,而不需要关心策略的具体实现。

#include <iostream>  
#include <memory>

// 步骤1: 定义策略接口  
class Strategy {
public:
	virtual ~Strategy() {}
	virtual void execute() = 0;
};

// 步骤2: 实现具体策略类  
class ConcreteStrategyA : public Strategy {
public:
	void execute() override {
		std::cout << "Executing strategy A" << std::endl;
	}
};

class ConcreteStrategyB : public Strategy {
public:
	void execute() override {
		std::cout << "Executing strategy B" << std::endl;
	}
};

// 步骤3: 创建上下文类  
class Context {
public:
	// 通过构造函数设置策略  
	Context(std::unique_ptr<Strategy> strategy) : strategy(std::move(strategy)) {}

	// 执行策略  
	void executeStrategy() {
		if (strategy) {
			strategy->execute();
		}
		else {
			std::cout << "No strategy is set." << std::endl;
		}
	}

	// 更改策略  
	void setStrategy(std::unique_ptr<Strategy> newStrategy) {
		strategy = std::move(newStrategy);
	}

private:
	std::unique_ptr<Strategy> strategy; // 使用unique_ptr管理策略对象的生命周期  
};

// 步骤4-6: 在客户端代码中设置和执行策略  
int main() 
{
	// 创建具体策略对象并使用unique_ptr管理  
	auto strategyA = std::make_unique<ConcreteStrategyA>();
	auto strategyB = std::make_unique<ConcreteStrategyB>();

	// 创建上下文对象并设置初始策略  
	Context context(std::move(strategyA));
	context.executeStrategy(); // 输出:Executing strategy A  

	// 更改策略  
	context.setStrategy(std::move(strategyB));
	context.executeStrategy(); // 输出:Executing strategy B  

	// 此时strategyA和strategyB已经被unique_ptr自动释放  

	return 0;
}

假设公司是有一个算税法的函数。

不同的国家有不同的算税法的逻辑。

刚开始有三个国家,china,us,japan

你可能这么写:

//不同的国家有不同的算税法的逻辑。各个国家会根据不同销售额计算税率
//
//刚开始有三个国家,china,us,japan
//
//你可能这么写:

#include <iostream>
using namespace std;

enum TaxCountry {
	china_tax,
	us_tax,
	japan_tax
};

//销售额类
class SaleOrder {
public:
	SaleOrder(int oneyeassalenum) {
		onesalemoney = oneyeassalenum;
	}

	int calculateTax() {
		if (taxcoutury== china_tax) {
			//如果是中国
			return onesalemoney / 3;
		}
		else if (taxcoutury == us_tax) {
			return onesalemoney / 2;
		}
		else if (taxcoutury == japan_tax) {
			return onesalemoney / 4;
		}
	}

	void setTaxCountry(TaxCountry _taxcoutury) {
		taxcoutury = _taxcoutury;
	}

private:
	TaxCountry taxcoutury;
	int onesalemoney;
};

int main()
{
    std::cout << "Hello World!\n";
	SaleOrder so(100);
	so.setTaxCountry(TaxCountry::china_tax);
	int result = so.calculateTax();
	cout << "result = " << result << endl;
}

但是随着业务的扩展,我们可能还要做德国的,法国的,印度的

那么这就要改动代码了,不符合开闭原则。

使用 策略模式改动

// 005策略模式.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//假设我们是做
//不同的国家有不同的算税法的逻辑。各个国家会根据不同销售额计算税率
//
//刚开始有三个国家,china,us,japan
//
//你可能这么写:

#include <iostream>
using namespace std;

enum TaxCountry {
	china_tax,
	us_tax,
	japan_tax
};

//销售额类
class SaleOrder {
public:
	SaleOrder(int oneyeassalenum) {
		onesalemoney = oneyeassalenum;
	}

	int calculateTax() {
		if (taxcoutury== china_tax) {
			//如果是中国
			return onesalemoney / 3;
		}
		else if (taxcoutury == us_tax) {
			return onesalemoney / 2;
		}
		else if (taxcoutury == japan_tax) {
			return onesalemoney / 4;
		}
	}

	void setTaxCountry(TaxCountry _taxcoutury) {
		taxcoutury = _taxcoutury;
	}

private:
	TaxCountry taxcoutury;
	int onesalemoney;
};

int main005()
{
    std::cout << "Hello World!\n";
	SaleOrder so(100);
	so.setTaxCountry(TaxCountry::china_tax);
	int result = so.calculateTax();
	cout << "result = " << result << endl;

	return 0;
}



// 策略基类
class TaxStrategy {
public:
	virtual int calculateTax(int onesalemoney) = 0;
	virtual ~TaxStrategy() {}
};

// china策略实现
class ChinaTaxStrategy : public TaxStrategy {
public:
	int calculateTax(int onesalemoney) override {

		cout << "china calculateTax" << endl;
		return onesalemoney / 3;
	}
};

// us 策略实现
class USTaxStrategy : public TaxStrategy {
public:
	int calculateTax(int onesalemoney) override {

		cout << "us calculateTax" << endl;
		return onesalemoney / 2;
	}
};

//japan 策略实现
class JapanTaxStrategy : public TaxStrategy {
public:
	int calculateTax(int onesalemoney) override {

		cout << "japan calculateTax" << endl;
		return onesalemoney / 4;
	}
};

//法国策略实现
class FranceTaxStrategy : public TaxStrategy {
public:
	int calculateTax(int onesalemoney) override {

		cout << "france calculateTax" << endl;
		return onesalemoney / 5;
	}
};

// 上下文类
class Context {
private:
	TaxStrategy* strategy;
	int oneyearmoney;
public:
	Context(TaxStrategy* s, int money) : strategy(s), oneyearmoney(money){}

	int ContextInterface() {
		return strategy->calculateTax(oneyearmoney);
	}
};


int main() {

	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口

	TaxStrategy * ts = NULL;
	ts = new ChinaTaxStrategy();

	int result  = ts->calculateTax(1000);
	cout << "result = " << result << endl;

	delete ts;



	//	也可以通过创建一个上下文来处理
		// 创建具体策略对象
	TaxStrategy* strategyA = new ChinaTaxStrategy();
	TaxStrategy* strategyB = new USTaxStrategy();

	// 设置策略并调用
	Context* contextChina = new Context(strategyA,10000);
	result = contextChina->ContextInterface();

	cout << "result = " << result << endl;


	Context* contextUS = new Context(strategyB,200000);
	result = contextUS->ContextInterface();

	cout << "result = " << result << endl;

	delete contextUS;
	delete contextChina;
	delete strategyB;
	delete strategyA;

	return 0;
}

上一篇:Faiss原理和使用总结-    1.数据准备:首先需要将数据转换为高维向量,这些向量可以是图像、文本或商品的embeddings。    2.建立索引:使用Faiss提供的索引结构对向量进行索引,以便快速检索。    3.相似度查询:在实际应用中,如文本召回,可以通过Faiss快速找到与给定query最相似的top k个商品或文档。    4.性能优化:Faiss通过量化和高效的搜索算法显著降低了相似度查询的时间复杂度,提高了查询每秒(QPS)的处理能力。 Faiss的优势:


下一篇:让新手变中手的ChatGPT 使用方法