文章目录
策略(Strategy)模式
隶属类别——对象行为型模式
1. 意图
定义一系列算法(即算法组),分别封装起来,让他们之间可以互相替换,此模式可以让算法独立于它的客户变化
2. 别名
Policy(政策)
3. 动机
有许多算法可对一个正文流进行分析,将这些算法硬编(硬编码在计算机程序或文本编辑中,硬编码是指将可变变量用一个固定值来代替的方法。 用这种方法编译后,如果以后需要更改此变量就非常困难了。 大部分程序语言里,可以将一个固定数值定义为一个标记,然后用这个特殊标记来取代变量名称.软编码是一种设计,而硬编码不过是一种具体的实现)进入使用他们的类是不可取的其原因如下:
- 需要换行的功能的客户程序如果直接包含换行算法代码的话将会变得复杂,这使得客户程序庞大而且难以维护,尤其当需要支持多种换行算法时问题会更加严重。
- 不同的时候需要不同的算法,我们不想支持我们并不适用的换行算法
- 当换行功能是客户程序的一个难以分割的成分是,增加新的换行算法或者改变现有算法将十分困难。
我们可以定义一个类来封装不同的换行算法,从而避免这些问题。一个以这种算法封装的算法成为策略(strategy)如下图所示:
假设一个Comoisiton(means 排版)类负责维护和更新一个正文浏览程序中显示的中文换行。换行策略不是Compostior类实现的,而是抽象的Compositor类的子类独立实现的,Compositor各个子类实现不同的换行策略:
-
SimpleCompositor实现一个简单的策略。它一次决定一个换行位置。
-
TeXCompositor实现查找换行位置的TEX算法。这个策略尽量全局地优化换行,也就是,一次处理一段文字的换行。
-
ArrayCompositor实现一个策略,该策略使得每一行都含有一个固定数目的项例如,用于对一系列的图标进行分析
Composition持有对Compositor的一个引用,一旦Composition重新格式化它的正文,它就将这个职责转发给它的Compositor对象.Composition的客户指定应该使用哪一种Compositor的方式是直接将它想要的Compositor装入Composition中。
4. 适用性
当存在以下情况时使用Strategy模式
- 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不通辩题。例如,你可能会定义一些反映不通的鸣叫的方式或者飞行方式,当这些变体实现为一个算法的类层次时,可以使用策略模式
- 算法使用客户不应该知道的数据,可使用策略模式以避免暴露复杂的,与算法相关的数据结构
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,将相关的条件分支移入它们各自的strategy类以代替这些条件语句。
5. 结构
6. 参与者
-
Strategy(策略,如FlyBehavior,QuackBehavior)
——定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法
-
ConcreteStrategy(具体策略,如FlyWithWing,FlyNoWay,Quack,Squeak,MuteSqueak)
——以Strategy接口实现某具体算法
-
Context(上下文,如Duck)
- 用一个ConcreteStrategy对象来完成具体操作
- 维持一个队Strategy对象的引用
- 可定义一个接口(这个接口不一定是真正的接口,可理解为入口把)让Strategy访问它的数据
7. 协作
- Strategy和Context相互作用以实现指定的算法。当算法被调用时,Context可以将该算法所需要的所有数据都传递给Strategy(例如在ConcreteStrategy使用Context中定义的方法比如getData(){}去获取所需要的数据)。或者,Context可以将自身作为一个参数传递给Strategy(如Duck鸭子中的构造方法,有点将自身传递给Strategy的意思),总之让Context的必要的数据传递给Strategy比如ConcreteStrategyA()为加法,ConcreteStrategyB为减法,ConcreteStrategyC为乘法,将两个数据传递给Strategy,总是就是让State和Context互相作用以决定具体的算法。
- Context将他的客户请求转发给它的Strategy(委托).客户通常创建并传递一个ConcreteStrategy对象给该Context(就像上述的Duck构造进行Behavior的初始化和SetFlyBehavior()和SetQuackBehavior());这样客户仅与Context交互。通常有一系列的ConcreteStrategy类可供客户选择。
8. 效果
Strategy模式有下面一些优点和缺点
优点:
-
1.相关算法系列 Strategy类层级为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法的公共功能。
-
2.一个代替继承的方法 继承提供了另一种支持多种算法或行为的方法。你可以直接生成一个Context类的子类,从而给它以不同的行为。但这会将行为硬行编制到Context中,而将算法的实现和Context的实现混合起来,从而使Context难以理解、难以维护和难以扩展,而且还不能动态地改变算法。最后你得到一堆相关的类,它们之间的唯一差别是它们所使用的算法或者行为。将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。
-
3.消除了一些条件语句 Strategy模式提供了用条件语句选择所需要的行为以外的另一种选择。当不同的行为堆砌在一个类中是,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句
例如,不用Strategy,正文换行的代码可能是像下面这样
// 可能这么写有问题,Java中好像不能这么判断对象,要使用instanceof关键字。 void performFly(Duck duck) { switch (duck) { case MillardDuck: millarDuck.fly(); break; case RedHeadDuck: redHeadDuck.fly(); break; //... } }
采用Strategy模式将换行的任务委托给一个Strategy对象从而消除了这些case语句:
void performFly() { flyBehavior.fly(); }
-
4.实现的选择 Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间/空间权衡取舍要求从不同的策略中进行选择。
缺点:
- 1.客户必须了解不同的Strategy 一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此,仅当这些不同的行为变体与客户相关的行为是,才需要使用Strategy模式。
- 2.Strategy个Context之间的通信开销 无论各个ConcreteStrategy实现的算法是简单还是复杂,它们都共享Strategy定义的接口.简单的ConcreteStrategy不会用到所有通过这个接口传递给它们的信息;这就意味着有事Context会创建和初始化一些它们不会用到的参