栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【学习笔记】C++下的策略模式(程杰-大话设计模式)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【学习笔记】C++下的策略模式(程杰-大话设计模式)

引入:设计一个商场收银软件,营业员根据顾客购买的商品单价及数量,向客户收费。


一、收银系统V1.0

CashRegisterSystem1_0.h

#pragma once
#include
#include
#include



using namespace std;

class CashRegisterSystem
{
private:
	double m_total; // 商品总价
	double m_totalPrice; // 当前商品总价
	int m_number; // 商品数量
	double m_price; // 商品单价
	vector shopVector;
	string shoplist; // 单次购物结果
public:

	void showList();

	void btnOk_Click(int number, double price);

	void btnClr_Rst();


};

CashRegisterSystem1_0.cpp

#include"CashRegisterSystem1.h"

void CashRegisterSystem::btnOk_Click(int number, double price)
{
	m_number = number;
	m_price = price;
	m_totalPrice = m_number * m_price;
	m_total += m_totalPrice;
	shoplist = "商品数量:" + to_string(m_number) + " 商品单价:" + to_string(m_price)
		+ " 商品总价:" + to_string(m_totalPrice);
	shopVector.push_back(shoplist);
	shopVector.push_back("当前商品总价:" + to_string(m_total));
}

void CashRegisterSystem::btnClr_Rst()
{
	shopVector.clear();
}

void CashRegisterSystem::showList()
{
	for (auto it = shopVector.begin(); it != shopVector.end(); it++)
	{
		cout << (*it) << endl;
	}
}

客户端程序:

#include
#include"CashRegisterSystem1.h"

using namespace std;

int main()
{
	CashRegisterSystem* crs = new CashRegisterSystem;

	crs->btnOk_Click(10, 200);
	crs->btnOk_Click(6, 18);
	crs->btnOk_Click(1, 800);

	crs->showList();
	
	system("pause");
	crs->btnClr_Rst();
	crs->showList();
	crs = NULL;

	delete crs;

	return 0;

}

运行结果:

 问题:

如果现在商场要打折,怎么办?折扣可能有很多种类?

改进方案1,通过使用switch语句,针对不同折扣在总价上乘以不同的系数。带来的问题,代码重复率太高,每个switch下的语句除了打折率,剩下都是相同的。此外,如果又提出新的打折方案,还需要进行修改。

改进方案2,使用简单工厂类。通过工厂类针对不同打折方案产生不同子类。带来的问题,产生的子类过多。

重点:面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。

二、收银系统V2.0

即便打折形式五花八门,但是打折的方法(算法)是一样的。 

CashSuper.h  超级收费方法,收费方法的抽象类

#pragma once
#include

using namespace std;

// 现金收费抽象类
class CashSuper
{
public:
	virtual double acceptCash(double money) { return money; };
};

CashNormal.h 正常收费模式

#pragma once
#include
#include"CashSuper.h"

using namespace std;

// 正常收费模式
class CashNormal : public CashSuper
{
public:
	double acceptCash(double money);
};

CashNormal.cpp

#include "CashNormal.h"

double CashNormal::acceptCash(double money)
{
	return money;
}

CashRebate.h 打折收费方式

#pragma once
#include
#include"CashSuper.h"

using namespace std;

// 打折模式
class CashRebate : public CashSuper
{
public:
	CashRebate(double moneyRebate);
	double acceptCash(double money);
private:
	double m_moneyRebate = 1;
};

CashRebate.cpp

#include "CashRebate.h"

CashRebate::CashRebate(double moneyRebate)
{
	m_moneyRebate = moneyRebate;
}

double CashRebate::acceptCash(double money)
{
	return money * m_moneyRebate;
}

CashReturn.h 返利收费模式

#pragma once
#include
#include"CashSuper.h"

using namespace std;

// 购物返利模式
class CashReturn :public CashSuper
{
private:
	double m_moneyCondition = 0.0;
	double m_moneyRetrun = 0.0;
public:
	// @param: moneyCondition 满减条件
	// @param: moneyReturn 返利值
	// 每满300-100,则moneyCondition为300;
	// moneyReturn 为 100.
	CashReturn(double moneyCondition, double moenyReturn);
	double acceptCash(double money);
};

CashReturn.cpp

#include "CashReturn.h"

CashReturn::CashReturn(double moneyCondition, double moneyReturn)
{
	m_moneyCondition = moneyCondition;
	m_moneyRetrun = moneyReturn;
}

double CashReturn::acceptCash(double money)
{
	double result = money;
	if (money >= m_moneyCondition)
	{
		result = money - int(money / m_moneyCondition) * m_moneyRetrun;
	}
	return result;
}


CashFactory.h 收费模式工厂

#pragma once
#include
#include
#include"CashSuper.h"
#include"CashNormal.h"
#include"CashRebate.h"
#include"CashReturn.h"

using namespace std;

class CashFactory
{
public:
	static CashSuper* createCashAccept(string type)
	{
		CashSuper* cs = NULL;
		map type2IntMap =
		{
			{"正常收费",1},
			{"满300返100",2},
			{"打8折",3},
		};
		int typeValue = type2IntMap[type];
		switch (typeValue)
		{
		case 1:
			cs = new CashNormal;
			break;
		case 2:
			cs = new CashReturn(300,100);
			break;
		case 3:
			cs = new CashRebate(0.8);
			break;
		default:
			break;
		}
		return cs;
	}
};

【Mark】学习点:C++ switch中case不接受string类型,可以使用map来讲string类型变量与int值一对一绑定。 

客户端:

#include
#include"CashFactory.h"
#include"CashSuper.h"
#include"CashNormal.h"
#include"CashRebate.h"
#include"CashReturn.h"

using namespace std;

void test01()
{
	double totalPrices = 0.0;

	CashFactory* cf = new CashFactory;
	CashSuper* csuper;

	// 不打折
	csuper = cf->createCashAccept("正常收费");
	totalPrices = csuper->acceptCash(700);
	cout << "totalPrice:" << totalPrices << endl;
	cf = NULL;
	csuper = NULL;
	delete cf;
	delete csuper;
}

void test02()
{
	double totalPrices = 0.0;

	CashFactory* cf = new CashFactory;
	CashSuper* csuper;

	// 不打折
	csuper = cf->createCashAccept("满300返100");
	totalPrices = csuper->acceptCash(700);
	cout << "totalPrice:" << totalPrices << endl;
	cf = NULL;
	csuper = NULL;
	delete cf;
	delete csuper;
}

void test03()
{
	double totalPrices = 0.0;

	CashFactory* cf = new CashFactory;
	CashSuper* csuper;

	// 不打折
	csuper = cf->createCashAccept("打8折");
	totalPrices = csuper->acceptCash(700);
	cout << "totalPrice:" << totalPrices << endl;
	cf = NULL;
	csuper = NULL;
	delete cf;
	delete csuper;
}

int main()
{
	//test01();
	//test02();
	test03();

	system("pause");
	return 0;
}

运行结果:

虽然通过简单工厂类,以及抽象的收费类可以产生五花八门的收费方式。

但是每次增删收费方法,都需要对简单工厂类的代码进行修改。这不是一种好的方式。

三、策略模式

策略模式(Strategy):它定义了算法家族,分别放封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。

 

 

 

四、策略模式下的收银系统V3.0

 CashSuper相当于一个抽象策略类;

CashNormal、CashRebate、CashReturn相当于三个具体的算法。

因此需要增加一个CashContext类。

CashContext.h

#pragma once
#include
#include"CashSuper.h"

using namespace std;

class CashContext
{
private:
	CashSuper* cs;
public:
	// 通过构造方法传入具体的收费策略
	CashContext(CashSuper* csuper);

	double GetResult(double money);
};

CashContext.cpp

#include "CashContext.h"

CashContext::CashContext(CashSuper* csuper)
{
	this->cs = csuper;
}

double CashContext::GetResult(double money)
{
	return cs->acceptCash(money);
}

客户端:

#include
#include
#include"CashSuper.h"
#include"CashContext.h"
#include"CashNormal.h"
#include"CashRebate.h"
#include"CashReturn.h"

using namespace	std;

void test01()
{
	CashContext* cc = NULL;
	cc = new CashContext(new CashNormal);
	double total = cc->GetResult(700);
	cout << total << endl;
	cc = NULL;
	delete cc;
}

void test02()
{
	CashContext* cc = NULL;
	cc = new CashContext(new CashRebate(0.8));
	double total = cc->GetResult(700);
	cout << total << endl;
	cc = NULL;
	delete cc;
}

void test03()
{
	CashContext* cc = NULL;
	cc = new CashContext(new CashReturn(300,100));
	double total = cc->GetResult(700);
	cout << total << endl;
	cc = NULL;
	delete cc;
}

int main()
{
	test01();
	test02();
	test03();

	system("pause");

	return 0;
}

运行结果:

五、策略与简单工厂类结合的收银系统V4.0

将简单工厂模式(SimpleFactory)和策略模式(Strategy)在CashContext中结合。

CashContextS_F.h

#pragma once
#include
#include
#include"CashSuper.h"
#include"CashNormal.h"
#include"CashRebate.h"
#include"CashReturn.h"

using namespace std;

// CashContextS_F means Strtegy plus SimpleFactory

class CashContextS_F
{
private:
	CashSuper* cs = NULL;
public:
	CashContextS_F(string type); // 传入string字符串, 而不是一个具体的策略对象

	double GetResult(double money);
};

CashContextS_F.cpp

#include "CashContextS_F.h"

CashContextS_F::CashContextS_F(string type)
{
	map type2IntMaP = 
	{
		{"正常收费",1},
		{"满300返100",2},
		{"打8折",3},
	};
	int typeValue = type2IntMaP[type];
	switch (typeValue) // 注意 “始化操作由‘case’标签跳过” 的错误
	{
	case 1:
	{
		CashNormal* cs0 = new CashNormal();
		cs = cs0;
		break;
	}
	case 2:
	{
		CashReturn* cs1 = new CashReturn(300, 100);
		cs = cs1;
		break;
	}
	case 3:
	{
		CashRebate* cs2 = new CashRebate(0.8);
		cs = cs2;
		break;
	}
	default:
		break;
	}
}

double CashContextS_F::GetResult(double money)
{
	return cs->acceptCash(money);
}

【Mark】 记录一个错误: error C2360: “XX ”的初始化操作由“case”标签跳过。

解决办法就是把初始化变量的case语段都用“{}”括起来。

客户端:

#include
#include
#include"CashSuper.h"
#include"CashContext.h"
#include"CashNormal.h"
#include"CashRebate.h"
#include"CashReturn.h"
#include"CashContextS_F.h"

using namespace	std;

void test01S_F()
{
	CashContextS_F* csuper = new CashContextS_F("正常收费");
	double total = csuper->GetResult(700);
	cout << total << endl;
	csuper = NULL;
	delete csuper;
}

void test02S_F()
{
	CashContextS_F* csuper = new CashContextS_F("打8折");
	double total = csuper->GetResult(700);
	cout << total << endl;
	csuper = NULL;
	delete csuper;
}

void test03S_F()
{
	CashContextS_F* csuper = new CashContextS_F("满300返100");
	double total = csuper->GetResult(700);
	cout << total << endl;
	csuper = NULL;
	delete csuper;
}


int main()
{
	//test01();
	//test02();
	//test03();

	test01S_F();
	test02S_F();
	test03S_F();

	system("pause");

	return 0;
}

运行结果:

 如此,客户端的代码更加简洁。同时客户端不需要认识(包含那么多头文件)更多的类。

六、策略模式总结

策略模式是一种定义了一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类直接的耦合。

优点:

Strategy类层为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。

策略模式简化了的单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。

策略模式封装了变化——实践中,可以用来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

参考资料:《大话设计模式》,作者:程杰

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/879204.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号