设计模式-策略模式

1 策略模式

  • 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
  • 将算法与其它部分分离开,只定义与算法相关的接口,然后在程序中以委托的方式来使用。使用委托这种弱关联关系可以很方便地整体替换算法。程序运行过程中也可以替换算法
  • 策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。

2 问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //更改:新添加
};

class SalesOrder{
TaxBase tax;
public:
double CalculateTax(){
//...

if (tax == CN_Tax){
//CN***********
}
else if (tax == US_Tax){
//US***********
}
else if (tax == DE_Tax){
//DE***********
}
else if (tax == FR_Tax){ //更改
//...
}
//....
}
};

3 策略v1

策略模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 抽象基类
class TaxStrategy{ // 一般就放一个方法,当然也会放几个相关的方法,但是一般方法数都不多
public:
virtual double Calculate(const Context& context)=0;
virtual ~TaxStrategy(){}
};

// 具体类
class CNTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
// ***********
}
};

class USTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
// ***********
}
};

class DETax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
// ***********
}
};

// 从而方便地进行扩展
// *********************************
class FRTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//.........
}
};

class SalesOrder{ // 稳定
private:
TaxStrategy* strategy;

public:
SalesOrder(StrategyFactory* strategyFactory){
this->strategy = strategyFactory->NewStrategy(); // 工厂模式
}
~SalesOrder(){
delete this->strategy;
}

public double CalculateTax(){
//...
Context context();
double val = strategy->Calculate(context); //多态调用
//...
}
};

4 策略v2(可执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
* The Strategy interface declares operations common to all supported versions
* of some algorithm.
*
* The Context uses this interface to call the algorithm defined by Concrete
* Strategies.
*/
class Strategy {
public:
virtual ~Strategy() = default;
virtual std::string doAlgorithm(const std::string& data) const = 0;
};

// The Context defines the interface of interest to clients.
class Context {
/**
* The Context maintains a reference to one of the Strategy
* objects. The Context does not know the concrete class of a strategy. It
* should work with all strategies via the Strategy interface.
*/
private:
std::unique_ptr<Strategy> strategy_;
/**
* Usually, the Context accepts a strategy through the constructor, but also
* provides a setter to change it at runtime.
*/
public:
explicit Context(std::unique_ptr<Strategy> &&strategy = {}) : strategy_(std::move(strategy)) {}

// Usually, the Context allows replacing a Strategy object at runtime.
void set_strategy(std::unique_ptr<Strategy> &&strategy) {
strategy_ = std::move(strategy);
}
/**
* The Context delegates some work to the Strategy object instead of
* implementing +multiple versions of the algorithm on its own.
*/
void doSomeBusinessLogic() const {
if (strategy_) {
std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)\n";
std::string result = strategy_->doAlgorithm("aecbd");
std::cout << result << "\n";
}
else {
std::cout << "Context: Strategy isn't set\n";
}
}
};

/**
* Concrete Strategies implement the algorithm while following the base Strategy
* interface. The interface makes them interchangeable in the Context.
*/
class ConcreteStrategyA : public Strategy {
public:
std::string doAlgorithm(const std::string& data) const override {
std::string result(data);
std::sort(std::begin(result), std::end(result));

return result;
}
};
class ConcreteStrategyB : public Strategy {
std::string doAlgorithm(const std::string& data) const override {
std::string result(data);
std::sort(std::begin(result), std::end(result), std::greater<>());

return result;
}
};
/**
* The client code picks a concrete strategy and passes it to the context. The
* client should be aware of the differences between strategies in order to make
* the right choice.
*/

void clientCode() {
Context context(std::make_unique<ConcreteStrategyA>());
std::cout << "Client: Strategy is set to normal sorting.\n";
context.doSomeBusinessLogic();
std::cout << "\n";
std::cout << "Client: Strategy is set to reverse sorting.\n";
context.set_strategy(std::make_unique<ConcreteStrategyB>());
context.doSomeBusinessLogic();
}

int main() {
clientCode();
return 0;
}

5 总结

  • Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
  • Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
  • 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。(单例模式)

6 参考