(二)、《大话设计模式》读书笔记 - 策略模式(C++实现)

策略模式介绍

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

===========================================分割线===================================

那么如何来理解什么是简单工厂模式?

==================================================分割线=========================

        将算法和对象分开,使算法独立于使用它的用户。将一个类中经常改变或将来可能改变的部分提取出来,作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以调用实现了这个接口类的行为。即准备一组算法。并将每个算法封装起来,使之可互换,策略算法是相同行为的不同实现。

该模式中包含的三个重要角色及其职责如下:

(1)抽象策略角色(Strategy)策略类,通常由一个接口或者抽象类实现

(2)具体策略角色(ConcreteStrategy):包装了相关的算法和行为

(3)环境角色(Context)持有一个策略类的引用(指针),最终给客户端调用具体算法

是不是很难懂?举个简单的例子。

==================================================分割线===========================

    比如我们使用手机肯定都会选择运营商,不同的定制版手机只能使用特定的运营商。总的来说,在这个例子中,选择运营商可以认为是一个抽象策略,而使用中国移动、中国联通、中国电信就是具体的策略。我们在正常使用的时候都是手机自己选择运营商而不是自己去实现,这里我们就可以用环境角色,其有一个算法的接口,可以根据手机版本,自己选择合适的运营商。这样我们只要知道手机版本,就可以自适应的选择运营商。

下面用代码来实现这个简单的例子。

==================================================分割线============================

/*
*策略模式的一个简单的例子(手机开机后运营商选择)
*编译环境:Visual Studio 2013 / win7
*作者:RMYHYMR
*时间:2018 / 6 / 30
*QQ:1196727000
*/

//文件名main.cpp

#include <iostream>
using namespace std;
//抽象策略类Honour,有一个抽象算法welcome
class Honour
{
public:
	virtual void welcome()
	{	//仅仅输出一行提示信息
		cout << "Welcome..." << endl;
	}
};
//具体策略类ChinaMobileHonour,有一个具体算法welcome
class ChinaMobileHonour :public Honour
{
public:
	virtual void welcome()
	{	//重写虚函数
		cout << "Welcome to use China Mobile..." << endl;
	}
};
//具体策略类ChinaUnicomHonour,有一个具体算法welcome
class ChinaUnicomHonour :public Honour
{
public:
	virtual void welcome()
	{	//重写虚函数
		cout << "Welcome to use China Unicom..." << endl;
	}
};
//具体策略类ChinaTelecomHonour,有一个具体算法welcome
class ChinaTelecomHonour :public Honour
{
public:
	virtual void welcome()
	{	//重写虚函数
		cout << "Welcome to use China Telecom..." << endl;
	}
};

class Context
{
public:
	//策略类构造函数,初始化指向基类的指针
	Context(Honour * honour_ptr) :honour_pointer(honour_ptr) {}
	//子类具体算法的实现,通过基类指针调用(多态)
	void welcome()
	{	//基类指针调用(注意此处的基类指针,在创建策略对象时初始化为子类的对象)
		honour_pointer->welcome();
	}
private:
	//基类指针
	Honour *honour_pointer;
};

int main()
{
	char type = 'M';
	Context * context_ptr;
	//选择创建具体要使用的子类对象。
	switch (type)
	{
	case 'M':
		//创建一个策略类的指针
		//用子类(ChinaMobileHonour)的对象作为实参传递给基类的指针,作为策略类的参数。
		context_ptr = new Context(new ChinaMobileHonour());
		break;
	case 'U'://同上
		context_ptr = new Context(new ChinaUnicomHonour());
		break;
	case 'T'://同上
		context_ptr = new Context(new ChinaTelecomHonour());
		break;
	default:
		break;
	}
	//调用算法,此算法的可以相互替换(具体调用哪个由基类的指针指向所决定)
	context_ptr->welcome();
	cout << endl;
	return 0;
}

对上面的代码我们来简单分析一下

==========================================分割线==================================

    首先我们创建了一个抽象基类Honour类,有一个虚成员函数,是策略类的一个抽象的算法,功能主要是输出提示信息。然后定义了三个继承自基类的子类,每个子类都有一个与自己相关的算法,即输出与自己有关的提示信息。最后定义了一个环境角色,我们可以通过环境类对象来调用上面出现的具体策略类的对象的方法(算法)。

    在这个程序中,首先有抽象策略对象类,包含算法的抽象,然后是一系列继承自抽象策略类的具体策略类,每个包含自己的算法。我们对这些算法的使用是听过一个环境角色类,其构造函数为我们创建一个对象,而且对象的一个数据成员(作为参数)为指向抽象策略对象类,这样我们在主函数创建一个对象类的时候,就可以通过选择用子类对象来初始化这个指针。这样我们就可以在主函数用环境类对象直接调用自己的算法接口函数,实现算法的相互替换。

    在这里如果我们要添加新的算法,就要创建一个包含算法的具体策略类,然后在主函数修改选择判断,而其他所有的部分都不用改动,特别是环境角色类。这点和前面提到的简单工厂是有区别的,虽然二者的核心思想都是面向对象的多态特性。

下面看看在简单工厂中代码用策略模式来实现,把加减乘数看成是算法。

==========================================分割线==================================

/*策略模式的C++代码实现
*编辑器:Visual Studio 2013
*作者:RMYHYMR
*时间:2018/6/30
*QQ:1196727000
*注:此程序为了运行方便,有以下几点:
*1、没有把类的定义和类的实现分不同文件写;
*2、直接使用了类的实现在前,主程序实现在后;
*3、使用了using namespace std;
*4、异常处理简单化;
*5、注释庞杂冗余,可能还会有错误(为了阅读);
*6、程序功能不全,功能单一;
*7、写的不好,欢迎指正
*/

#include <iostream>
using namespace std;

class Operation
{
public:
	//默认构造函数,两个操作数设置为0
	Operation() :left_operand(0.0), right_operand(0.0) {};
	//初始化构造函数,初始化两个操作数
	Operation(double leftNum, double rightNum) :left_operand(leftNum), right_operand(rightNum) {}
	//默认虚析构函数(为了父类指针可以调用子类的析构函数,即多态)
	virtual ~Operation() {};

	//此函数为计算类的抽象方法函数,作为算法类抽象算法。
	//此函数计算两个数的结果,并将结果返回
	virtual double getResult()
	{	//result作为两个数的计算结果,基类抽象函数默认返回0
		double result = 0.0;
		return result;
	}
	//设置左操作数
	void setLeftOperand(double leftNum){ left_operand = leftNum; }
	//设置右操作数
	void setRightOperand(double rightNum){ right_operand = rightNum; }
	//设置两个操作数
	void setOperands(double leftNum, double rightNum)
	{
		left_operand = leftNum;
		right_operand = rightNum;
	}
protected:
	//抽象基类的两个数据成员,作为两个操作数
	double left_operand; //左操作数
	double right_operand;//右操作数
private:
	//无成员
};



//加法类,实现两个数相加,继承自抽象基类(其中的getResult函数为类的具体算法)
class AddOpeatinon :public Operation
{
public:
	//默认构造函数,将从基类继承的两个操作数设置为0
	AddOpeatinon() :Operation(0.0, 0.0) {}
	//初始化构造函数,初始化从基类继承的两个操作数
	AddOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
	// 默认虚析构函数(为了父类指针可以调用子类的析构函数,即多态)
	virtual ~AddOpeatinon(){}

	//重写基类计算函数,此函数计算两个数的结果,并将结果返回
	virtual double getResult()
	{	//此函数返回两个操作数的和
		return left_operand + right_operand;
	}
protected:
	//无成员
private:
	//无成员
};

//减法类, 实现原理见加法类
class SubOpeatinon :public Operation
{
public:
	SubOpeatinon() :Operation(0.0, 0.0) {}
	SubOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
	virtual ~SubOpeatinon() {}

	virtual double getResult()
	{
		return left_operand - right_operand;
	}
protected:
	//无成员
private:
	//无成员
};


//乘法类, 实现原理见加法类
class MulOpeatinon :public Operation
{
public:
	MulOpeatinon() :Operation(0.0, 0.0) {}
	MulOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
	virtual ~MulOpeatinon(){}

	virtual double getResult()
	{
		return left_operand * right_operand;
	}
protected:
	//无成员
private:
	//无成员
};
//除法类, 实现原理见加法类
class DivOpeatinon :public Operation
{
public:
	DivOpeatinon() :Operation(0.0, 0.0) {}
	DivOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
	virtual ~DivOpeatinon(){}

	virtual double getResult()
	{	//此处如果右操作数为0,则抛出一个double型的异常
		if (right_operand == 0)
			throw right_operand;
		return left_operand / right_operand;
	}
protected:
	//无成员
private:
	//无成员
};

//在类中,构造一个具体的算法类,并调用其中的算法。
class Context
{
public:
	//默认构造函数
	Context() = default;
	//构造函数,参数为基类的指针
	Context(Operation *base_ptr_temp) :operation_base_pointer(base_ptr_temp) {}
	//用基类的指针来调用子类的算法,此处作为一个统一的接口,可以调用不同的算法。
	double getRseult()
	{	
		return operation_base_pointer->getResult();
	}
protected:
private:
	//指向基类的指针
	Operation *operation_base_pointer;
};


int main()
{
	try{
		//定义两个操作数leftNum、rightNum并初始化为0
		double leftNum = 0.0, rightNum = 0.0;
		//定义一个运算符变量
		char binary_operator;
		//定义一个运算结果result并初始化为0
		double result = 0.0;

		//程序输入两个操作数的值和要进行的运算
		cout << "Please enter a floating-point left operand: ";
		cin >> leftNum;
		cout << "Please enter a floating-point right operand: ";
		cin >> rightNum;
		cout << "Please select the operation to be performed: ";
		cin >> binary_operator;
		//创建一个对象,用来统一调用
		Context * context;
		switch (binary_operator)
		{
			//加法
		case '+':
			//构造一个策略类对象(参数为基类的指针指向具体子类对象,这里也是多态的实现),用来调用子类不同的算法实现
			//使用时必须首先创建一个想使用的类对象(如加法),然后将该对象做为参数传递给策略类构造函数,最后通过使用策略类对象调用不同的算法
			context = new Context(new AddOpeatinon(leftNum, rightNum));	
			break;
			//减法
		case '-':
			context = new Context(new SubOpeatinon(leftNum, rightNum));
			break;
			//乘法
		case '*':
			context = new Context(new MulOpeatinon(leftNum, rightNum));
			break;
			//除法
		case '/':
			context = new Context(new DivOpeatinon(leftNum, rightNum));
		
			break;

		}
		//用策略类的对象来调用子类的具体算法
		result = context->getRseult();
		//输出运算结果
		cout << leftNum << " " << binary_operator << " " << rightNum << " = " << result << endl;

	
	}
	catch (...){
		cerr << "Error!!!" << endl;
		exit(1);
	}

	return 0;	
}

==========================================分割线====================================

最后还是来说说优缺点和使用场景。

==========================================分割线====================================

优点:

1、 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。
2、 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
3、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。

==========================================分割线====================================

缺点:

1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2、 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。

==========================================分割线====================================

使用环境:

1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

==========================================分割线====================================

==========================================分割线====================================




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值