一、什么是策略模式?
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式可以使算法的变化独立于使用它的客户。
策略模式根据功能大致分为三个类:
- 抽象策略类(Strategy)
- 具体策略类(ConcreteStrategy)
- 环境类(Context)
二、不使用策略模式的例子
理论可能有一些抽象,直接来看具体代码的例子。
如果我有一个context
类,这个类想同时使用2种不同的策略,那我该怎么办?
最简单的方法是下面这样:
首先我新建2个不同的策略类AB:
class ConcreteStrategyA { // 策略A
public:
void execute() override {
// 具体算法A
}
};
class ConcreteStrategyB { // 策略B
public:
void execute() override {
// 具体算法B
}
};
// ...还有策略CDEF等等
context
类中组合策略类AB:
class Context {
private:
ConcreteStrategyA* strategyA_;
ConcreteStrategyB* strategyB_;
public:
Context(ConcreteStrategyA* strategyA, ConcreteStrategyB* strategyB) {
strategyA_ = strategyA;
strategyB_ = strategyB;
}
void businessMethodA() {
// 调用策略算法A
strategyA_->execute();
}
void businessMethodB() {
// 调用策略算法B
strategyB_->execute();
}
};
如果这时候我想再增加一些其它策略,那我需要单独写一个策略类C,然后更改context
类中,再加一个策略C,再写一个策略C调用的算法void businessMethodC()
。可以看出这种方法耦合程度高,不太好。
此外如果我们有strategyA,strategyB,strategyC
等100个策略呢,总不能在context
中为每一个策略都单独写一个businessMethod(){}
函数吧,这时候策略模式就登场啦!!!
三、使用策略模式的例子
我们可以把上面的所有策略都继承同一个抽象父类,context
中只组合抽象父类,借用父类指针指向不同的子类对象来调用不同的策略,下面来看实现:
1.抽象策略类(Strategy),是具体策略类的父类
class Strategy {
public:
virtual void execute() = 0;
};
2.具体策略类(ConcreteStrategy),封装了策略具体要怎么实现
class ConcreteStrategyA : public Strategy { // 策略A
public:
void execute() override {
// 具体算法A
}
};
class ConcreteStrategyB : public Strategy { // 策略B
public:
void execute() override {
// 具体算法B
}
};
3.环境类(Context),维护一个策略对象,提供执行业务的方法
class Context {
private:
Strategy* strategy_; // 不用继承用组合,直接组合最顶层的抽象策略类
public:
// 根据父类指针指向的不同子类对象,可以调用不同的策略方法
Context(Strategy* strategy) : strategy_(strategy) {}
// 切换策略的函数,同一个对象可以使用不同策略
void setStrategy(Strategy* strategy) {
this->strategy_ = strategy;
}
void businessMethod() {
// 调用策略算法
strategy_->execute();
}
};
4.客户端调用
Strategy* Strategy = new ConcreteStrategyA(); // 新建抽象父类策略指针,指向子类策略A
Context* ctx = new Context(Strategy); // 新建环境类,调用策略算法
ctx->businessMethod();
// ctx切换策略为B
ctx->setStrategy(new ConcreteStrategyB());
ctx->businessMethod();
策略模式说白了,就是一个对象context
想使用不同的策略,但是又不想单独建立一大堆策略类ABCD…,这时候就可以单独抽象一个父类来供context
调用。
虽然看起来策略模式比第一种实现代码量大一些,但是如果我们想要新加一些策略CDEF,我们并不需要更改context
类,只需要单独写一个策略C的类,然后继承抽象策略类,在context
中同样可以调用新加的策略。
四、总结
策略模式优点:
- 策略算法可以自由切换:策略模式可以方便地切换不同的算法或策略,而不需要修改
Context
类的代码。 - 算法和使用算法的客户解耦:策略类封装算法,
Context
和策略类解耦,客户只需要决定用哪个策略即可。 - 遵循开闭原则:可以在不修改原代码的情况下引入新算法,扩展系统功能。
策略模式缺点
- 会增加许多策略类:每新增一个算法就需要增加一个新的策略类。
- 增加了系统复杂度:需要了解所有策略类的不同之处,给客户带来额外的复杂性。
- 客户必须理解策略之间的差异:否则无法选择合适的策略。