1. 策略模式的简介
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。
——《设计模式》GoF
- 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
- 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
2. 策略模式的结构
- 环境(Context)角色:持有一个Strategy类的引用。
- 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(ConcreteStrategy)角色:实现了相关的算法或行为。
基本架构:
典型的抽象策略类代码:
abstract class AbstractStrategy
{
public abstract void Algorithm(); //声明抽象算法
}
典型的具体策略类代码:
class ConcreteStrategyA : AbstractStrategy
{
//算法的具体实现
public override void Algorithm()
{
//算法A
}
}
典型的环境类代码:
class Context
{
private AbstractStrategy strategy; //维持一个对抽象策略类的引用
public void SetStrategy(AbstractStrategy strategy)
{
this.strategy = strategy;
}
//调用策略类中的算法
public void Algorithm()
{
strategy.Algorithm();
}
}
典型的客户端代码片段:
……
Context context = new Context();
AbstractStrategy strategy;
strategy = new ConcreteStrategyA(); //可在运行时指定类型,通过配置文件和反射机制实现
context.SetStrategy(strategy);
context.Algorithm();
……
3. 策略模式的总结
- 算法并不是孤立的,它通常都需要有一些上下文(ContextInterface())去调用它,或者是传入一些参数。Strategy类型里面不携带状态信息(这是与模板方法的区别,模板方法本身是携带状态信息的),我们不能把它看成一种实例,即使有状态,也是会通过参数传入。一个Strategy定义了一个算法的完整步骤和结构,只要用一个Strategy具体类,就可以完成整个算法的操作,不会有其它依赖和耦合。Context和Strategy是一个对象组合的使用关系。Strategy中的抽象接口随时可以替换成具体的类,达到在不同算法之间动态地切换。
- 这个模式的核心是通过对象组合的方式把本来直接调用的内容委托给接口实体对象来完成,至于接口实体对象具体是什么,在运行时才知道,即是运行时改变。
- 何时使用何种具体策略角色,策略模式并不负责做这个决定。换言之,应当由客户端自己决定在什么情况下使用什么具体策略角色。策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中"退休"的方便。
4. 策略模式的优缺点和使用环境
优点:
- 提供了对开放封闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活的增加新的算法和行为。
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免重复的代码。
- 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不使用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。
- 使用策略模式可以避免使用多重条件转移语句(多重转移语句不易维护,比使用继承的办法还要原始和落后)。
- 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
- 策略模式造成很多的策略类,任何细小的变化都有可能增加一个新的具体策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式(用到再学吧)来减少对象的数量。
- 无法同时在客户端使用多个策略类,也就是说,在使用策略类时,客户端每次使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类完成剩余的功能。
适用环境:
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- 一个系统需要动态地在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。
- 一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复杂的和只与算法有关的数据。
- 如果一个对象有很多的行为,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象设计的概念。
用适当的组合关系来代替不应该有的继承关系,老生常谈了