Strategy 策略模式

要解决的问题:

在软件按的构建过程中, 一些对象使用的方法可能多种多样, 经常会出现变动, 如果将这个方法都编码到对象方法中, 将会高频率的修改对象方法, 使其变得异常复杂, 同时也破坏了代码尽量不去变动的准则, 并且有时候支持不会去使用的一些方法也是一个性能负担

模式定义:

定义一系列算法, 将它们一个个封装起来, 并且使它们可互相替(变化). 该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展, 子类化)

举例

当要实现一个计算税务的需求时, 没有进行优化前的代码如下

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
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){ // 如果是法国, 该如何计算税收
//...
}

//....
}

};

可以看到, 如果要新增国家的话, 那么就要在原来的代码上进行改动, 通过增加 else if 的形式进行源码级改动, 如果这种改动逐渐增多了之后, 将会使得可维护性大大降低, 而且一次增加将会影响大量的代码,

使用 Strategy 策略模式进行重构

考虑将计算税收的方法抽象出来, 通过继承的方式来对可能会增加的税收方式进行扩展, 这样只需要将不同计算税收的指针赋给父类的指针, 就可以利用多态来实现不同国家的税收计算

类结构

重构代码

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

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);
//...
}

};

要点总结

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