前言
在日常生活中,我们经常会遇到为完成某件事情,通常我们的方法都有很多种选择。例如,从上海去北京,我们可以坐火车,坐飞机或者自己开车去。事实上,在软件开发中也经常会有相类似的情况,当需要实现某一需求或功能时,可能存在多种算法。例如,在做一道算法题时,我们可以根据要求的时间复杂度或者空间复杂度采取不同的算法去实现。
假设这样一种场景,当我们要出门旅游时,去近的地方,我们可能选择自驾,去远一些的地方,我们可能选择高铁出行,去更远一些的地方或者出国旅游,我们可能会选择飞机。这样一个场景,采用程序来实现,我们可能会用switch或者ifelse的语句来对场景进行判断再选择合理的出行方式。但是如果条件语句多了之后就会变得比较复杂,并且代码可读性较低。但是如果采用策略模式来实现,就会很好的解决该问题。
策略模式的定义与结构
1.策略模式的定义
策略模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
2.策略模式的结构
策略模式的主要角色主要有以下几个:
1.抽象策略类:定义了一个公共的接口,其他具体的策略类都继承该抽象策略类并且各自以不同的方式实现该接口。
2.具体策略类:实现了抽象策略类的接口,提供了具体的算法实现。
3.环境类:具有一个策略类的引用,最终给客户端调用。
策略模式的重点是在于context类的定义,该类根据子类的不同,灵活的运行类中的多态指针来调用不同的算法。
策略模式的实现
接下来我们看一个简单的场景:
每次在购物付款的时候,我们都有好几种方式选择,例如微信支付,支付宝支付以及现金支付。我们现在以1代表支付宝支付,2代表微信支付,3代表现金支付。每个顾客在付款前都会告诉收银员付款方式,如果要使用一个程序来表征这三种付款方式,很容易就想到if/else或者switch。如下:
#include <iostream>
#include <string>
using namespace std;
class Payment
{
private:
string name;
int n;
public:
Payment(string str,int i)
{
name = str;
n = i;
}
void method()
{
switch(n)
{
case 1:
cout << name << "的付款方式:";
zfb();
break;
case 2:
cout << name << "的付款方式:";
weixin();
break;
case 3:
cout << name << "的付款方式:";
xianjin();
break;
}
}
void zfb()
{
cout << "use zhi fu bao" << endl;
}
void weixin()
{
cout << "use weixin" << endl;
}
void xianjin()
{
cout << "use xianjin" << endl;
}
};
int main()
{
Payment person1("zhangsan",2);
Payment person2("lisi",1);
Payment person3("bob",3);
person1.method();
person2.method();
person3.method();
return 0;
}
但是这样的代码虽然看上去简单,但是扩展性不好,例如新增了一种付款方式,就都要去修改method方法,一旦新增的内容多了,method接口就会变得非常臃肿。但是如果我们采用策略模式,函数的定义放在抽象基类中,其他各种付款方式的接口都继承这个基类。再封装一个context类来实现客户端的调用,具体实现如下:
#include <iostream>
using namespace std;
class Payment
{
public:
virtual void method() = 0;//抽象基类,定义了一个接口
};
class Zhifubao:public Payment//子类支付宝
{
public:
string name;
Zhifubao(string str)
{
name = str;
}
virtual void method()
{
cout << name << " use zhifubao " << endl;
}
};
class Weixin:public Payment//子类微信
{
public:
string name;
Weixin(string str)
{
name = str;
}
virtual void method()
{
cout << name << "use weixin" << endl;
}
};
class Xianjin:public Payment//子类现金
{
public:
string name;
Xianjin(string str)
{
name = str;
}
virtual void method()
{
cout << name << " use xianjin" << endl;
}
};
class Context
{
public:
Payment *pay;
Context(Payment* payment)
{
pay = payment;
}
void method()
{
pay->method();
}
};
int main()
{
Payment *zhangsan = new Weixin("zhangsan");
Payment *lisi = new Zhifubao("lisi");
Payment *bob = new Xianjin("bob");
Context a(zhangsan);
Context b(lisi);
Context c(bob);
a.method();
b.method();
c.method();
return 0;
}
由上述代码可以很清楚的看出采用策略模式之后,代码的扩展性比switch的方式好很多。对于新增的付款方式,我们只需要新增一个具体类并实现method方法就可以了,并不会影响到其他的代码块,这也体现了设计模式的原则和目的。
策略模式的优缺点
通过上面的例子可以总结出策略模式的优缺点:
1.优点:扩展性好;避免多重条件判断;各个算法接口之间是独立的互不影响
2.缺点:当增加策略时,也会相应的增加具体的策略类;所有的策略类都会对外暴露。