工厂在业务逻辑增加的过程中,也会越来越复杂,维护性逐渐降低。

策略模式

定义了算法家族,分别封装起来,让其可以互相替换。此模式让算法的变化不影响使用算法的客户。

核心在于算法是随时都可能相互替换的

  • Strategy类,定义所有支持的算法的公共接口
  • ConcreteStrategy类,封装具体算法或行为,继承自Strategy
  • Context类,使用一个ConcreteStrategy进行配置,维护一个对Strategy对象的引用
1
2
3
4
5
6
class Context
def __init__(self, strategy: Strategy):
self.strategy = strategy

def context_interface():
return self.strategy.algorithm_interface()

但在具体实现过程中,仍然不能完全将策略选择过程从客户端中剥离。可以考虑与简单工厂模式结合。

1
2
3
4
5
6
7
8
class CashContext:
def __init__(type: str):
# 接收策略类型而不是具体收费策略对象
# self.cash = ...
pass

def get_result(money: float):
return self.cash.get_result()

其与简单工厂的不同在于降低了客户端的使用门槛:

1
2
3
4
5
6
7
# 简单工厂需要认识两个类:`CashSuper`和`CashFactory`
cash_super: CashSuper = CashFactory.create_cash_accept(type)
result = cash_super.get_result(...)

# 策略模式+简单工厂只需要一个类:`CashContext`
cash_super: CashContext = CashContext(type)
result = cash_super.get_result(...)

策略模式本质上是定义一系列算法的方法,它们完成相同的工作,只是实现不同,我们可以用相同的方式调用所有的算法,从而降低算法类与使用算法类之间的耦合。

Strategy类层次为Context定义了一系列可供重用的算法或行为,继承有助于析出这些算法的公共功能。例如本例中的get_result

此外还简化了单元测试,因为每个算法都有自己的类,可通过自己的接口单独测试。每个算法课保证它没有错误,修改其中一个也不影响其他算法。

策略模式封装了变化,从而在客户端中消除大量条件语句:将特殊行为封装进独立的Strategy类。

实践中,策略模式可以用来几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

此外,策略模式本身而言,选用具体实现的职责由客户端对象承担,并转给Context对象,本身并不解除客户端选择判断的压力,而与工厂模式结合后,选择策略的职责由Context承担,最大化减轻客户端职责。

使用反射技术则可以在Context类中避免对switch语句的反复修改。

感想

策略模式似乎对简单工厂模式又做了一层抽象/隔离,避免用户直接接触到工厂类,同时提供了一个统一的接口。

Refector GURU

策略模式要求把做同样的事的不同方法分别抽象成不同的类,这些类称为 策略。上下文对象可以存储一个策略,并把任务交给策略执行。上下文对象并不知道该如何选择策略,client通过上下文来选择合适的策略。

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
class Strategy {
public:
virtual int execute(int a, int b) const = 0;
virtual ~Strategy() {}
};

class Add: public Strategy {
int execute(int a, int b) override {
return a + b;
}
};

class Subtract: public Strategy {
int execute(int a, int b) override {
return a - b;
}
};

// ...

class Context {
private:
Strategy* strategy; // 以值类型存储将导致*切片问题*
public:
~Context() {
if (strategy) delete strategy;
}
setStrategy(Strategy* s) {
strategy = s;
}
int executeStrategy(int a, int b) {
return strategy->execute(a, b);
}
};