策略模式介绍
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
===========================================分割线===================================
那么如何来理解什么是简单工厂模式?
==================================================分割线=========================
将算法和对象分开,使算法独立于使用它的用户。将一个类中经常改变或将来可能改变的部分提取出来,作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以调用实现了这个接口类的行为。即准备一组算法。并将每个算法封装起来,使之可互换,策略算法是相同行为的不同实现。
该模式中包含的三个重要角色及其职责如下:
(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;
}
==========================================分割线====================================
最后还是来说说优缺点和使用场景。
==========================================分割线====================================
优点:
==========================================分割线====================================
缺点:
==========================================分割线====================================
使用环境:
==========================================分割线====================================
==========================================分割线====================================