初探策略模式
代码链接
koonchen/design-patterns/strategy
要点
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。该模式使得算法可以独立于使用它的客户而变化。
假设我们现在需要对一个正文流进行分行,我们不会把算法硬编码到它们使用的类中,因为:
其一这会使得客户程序变得复杂,变得庞大并且难以维护;
其二不同的时候需要使用不同的算法,不使用的代码不需要写进程序里;
其三当某个算法是不可分割的一部分的时候,增加新的算法或者改变已有代码是非常困难的。
我们现在可以定义一些类来封装不同的换行算法,从而避免这些问题。一个以这种方法封装的算法称为一个策略:
思想很简单,实现也很简单,但组合在一起就变成了策略模式。
策略模式的适用性:
- 许多相关的类仅仅是行为有异。策略提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。例如,我们可能会定义一些反映不同的空间 / 时间权衡的算法。
- 算法使用客户不应该知道的数据。使用策略模式可以避免暴露复杂的、与算法相关的数据结构。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。
策略模式的参与者:
- Strategy — 定义所有支持的算法的公共接口。
- ConcreteStrategy — 以 Strategy 实现的具体算法。
- Context — 维护一个对 Strategy 对象的引用,可以定义一个接口来让 Strategy 来访问它的数据。
该模式的结构:
我们通过 Context
和 Strategy
的相互作用以实现选定的算法,当算法被调用, Context
可以将该算法所需要的所有数据都传递给该 Strategy
,或者, Context
可以将自身作为一个参数传递给 Strategy
操作。这就让 Strategy
在需要的时候可以回调 Context
。
Context
将它的客户的请求转发给它的 Stratege
。客户通常创建并传递一个 ConcreteStrategy
对象给该 Context
,这样客户仅与 Context
进行交互。
策略模式的优缺点:
- 该模式提供了相关算法系列的模式。
Strategy
类层次为Context
定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。- 这是一个替代继承的方法。继承提供了另外一种支持多种算法或行为的方法。我们可以直接生成一个
Context
类的子类,从而给它以不同的行为。但是继承是一种硬编码行为,这将使得Context
变得难以理解、难以维护,最后我们会得到一堆相关的类,它们唯一的差别仅仅是它们所使用的算法或行为。- 消除了一些条件语句。
Strategy
模式提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一起的时候,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy
类中消除了这些条件语句。- 对实现提供选择。该模式可以提供相同行为的不同实现,客户可以根据不同时间 / 空间的抉择来选择不同的策略。
- 缺点也有,比如客户必须了解不同的策略。客户选择一个合适的策略就必须要知道这些策略到底有何不同,此时可能不得不向客户暴露具体的实现。
- 通信开销。无论是各个
ConcreteStrategy
实现的算法是简单还是复杂,它们都共享Strategy
定义的接口,因此可能某些ConcreteStrategy
不会都用到所有通过这个接口传递给它们的信息,简单的实现可能不要使用到其中的任何信息,这就意味着有些通信是浪费的,耦合度还是略高。- 增加了对象的数目。策略模式增加了一个应用中的对象的数目,有时候你可以将
Strategy
实现为可供各个Context
共享的无状态的对象来减少这一开销。任何其余的状态都由Context
来维护。因为共享的Strategy
不应该在各名词调用之间维护状态。
实现
java 实现
1 | interface Compositor { |
把专业的事情交给专业的人,可能就是策略模式想告诉我们的。
相关模式
- Flyweight — 策略对象经常是很好的轻量级对象。
一看,确实享元模式和策略上是大同小异的,该书推荐我们用策略模式的对象到享元模式中,可以说它们是相辅相成的,享元模式就是在策略模式外面包一层对象,更好地处理策略对象。