策略模式的作用就是把具体的算法实现从业务逻辑中剥离出来,成为一系列独立的算法类,使得他们可以相互替换。
举个例子:平常生活中超时基本上都有活动,比如会员日全场9折,周年庆消费多少减多少,抽奖活动等等。
那么如果将业务具体落实在代码上,是不是可以用多个条件判断来封装具体的业务逻辑,if判断是一个解决方案,但是还有没有其他的解决方案呢?
可以使用策略模式来将其封装成特定的策略的类,一个策略一个类。
//抽象基类活动方法。
class Method
{
public:
Method() {
cout << "Method构造" << endl;
}
virtual ~Method() {
cout << "Method析造" << endl;
}
//提供的 活动结算方法接口。
virtual float calc(float &money) = 0;
};
//会员日活动
class VIPdayMethod:public Method
{
public:
VIPdayMethod() {
cout << "VIPdayMethod构造" << endl;
}
~VIPdayMethod() {
cout << "VIPdayMethod析造" << endl;
}
float calc(float &money)override {
if (money > 0) {
cout << "会员日做活动,全场9折!!!" << endl;
cout << "已经享受活动折扣 " << endl;
return money * 0.9;
}
return 0.0;
}
};
//周年庆活动
class Anniversary:public Method
{
public:
Anniversary() {
cout << "Anniversary构造" << endl;
}
~Anniversary() {
cout << "Anniversary析造" << endl;
}
float calc(float &money)override {
cout << "周年庆做活动,满500立减50" << endl;
cout << "您的购物金额为:" << money << endl;
if (money >= 500) {
cout << "已经享有周年庆活动" << endl;
return money - 50;
}
return money;
}
};
//超市结算,封装一个Method基类指针,以方便不同的子类活动 使用多态
class Market
{
public:
Market(Method* ptr){
this->mPtr_ = ptr;
cout << "Market构造" << endl;
}
~Market() {
cout << "Market析造" << endl;
}
void clacContext(float money) {
cout << "您需要支付: " << mPtr_->calc(money) << endl;
}
private:
//基类指针
Method* mPtr_ = nullptr;
};
int main()
{
Market* m1 = new Market(new VIPdayMethod());
m1->clacContext(200);
cout << "***************" << endl;
Market* m2 = new Market(new Anniversary);
m2->clacContext(500);
delete m1;
m1 = nullptr;
cout << "***************" << endl;
delete m2;
m2 = nullptr;
return 0;
}
这里需要注意
发现没有调用到子类VIP和Aniversary的析构函数。为什么?
如果单纯的delete m1只是释放Market的指针
因为:
Market类中封装了Method* mPtr_
而mPtr_又指向用户所创建的子类
所以怎么设计这个子类的delete释放操作呢?
只需要在Market类的析构函数中将mPtr_释放即可
~Market() {
if (mPtr_ != nullptr) {
delete mPtr_;
mPtr_ = nullptr;
}
cout << "Market析造" << endl;
}
那么调用到Market类析构函数时也就相应的释放掉mPtr_所指向的子类对象。
int main()
{
Market* m1 = new Market(new VIPdayMethod());
m1->clacContext(200);
cout << "***************" << endl;
Market* m2 = new Market(new Anniversary);
m2->clacContext(500);
delete m1;
m1 = nullptr;
cout << "***************" << endl;
delete m2;
m2 = nullptr;
return 0;
}
总结:
优点:
- 各自使用封装的算法,可以容易地引入新的算法来满足相同的接口
- 算法的细节完全封装在 Strategy 类中,因此可以在不影响 Context 类的情况下更改算法的实现
- 由于实现的是同一个接口,所以策略之间可以自由切换
缺点:
- 客户端必须知道所有的策略,了解它们之间的区别,以便选择恰当的算法