目录
一、概念以及结构图
1、定义
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 |
2、解决问题的思路
按照策略模式的方式,应该先把所有的计算方式独立出来,每个计算方式做成一个单独的算法类,从而形成一系列的算法,并且为这一系列算法定义一个公共的接口,这些算法实现是同一接口的不同实现,地位是平等的,可以相互替换。这样一来,要扩展新的算法就变成了增加一个新的算法实现类,要维护某个算法,也只是修改某个具体的算法实现即可,不会对其他代码造成影响。也就是说这样就解决了可维护、可扩展的问题。
为了实现让算法能独立于使用他的客户,策略模式引入了一个上下文的对象,这个对象负责持有算法,但是不负责决定具体选用哪个算法,把选择算法的功能交给了客户,由客户选择好具体的算法后,设置到上下文对象中,让上下文对象持有客户选择的算法,当客户通知上下文对象执行功能的时候,上下文对象则转调具体的算法。这样一来,具体的算法和直接使用算法的客户是分离的。
具体的算法和使用他的客户分离以后,使得算法可独立于使用它的客户而变化,并且能够动态地切换需要使用的算法,只要客户端动态地选择使用不同的算法,然后设置到上下文对象中去,在实际调用的时候,就可以调用到不同的算法。
3、结构图
上面的文字比较抽象,我们直接上结构图来方便理解。
Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。
ConcreteStrategy:具体的策略实现,也就是具体的算法实现。
Context:上下文,负责和具体的策略类交互。通常上下文会持有一个真正的策略实现,上下文还可以让具体的策略类来获取上下文的数据,甚至让具体的策略类来回调上下文的方法。
二、使用场景以及代码案例
1、使用场景
1、出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。
2、出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。
3、需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。
4、出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。
2、案例代码
案例基于结构图实现,如下:
1、创建Strategy接口以及实现类(每个策略为独立的类)
/** public class ConreteStrategyA implements Strategy { public class ConreteStrategyB implements Strategy { public class ConreteStrategyC implements Strategy { |
2、创建上下文
public class Context { |
3、客户端测试
public class Client { |
三、深入研磨
1.策略模式的功能
策略模式的功能是把具体的算法实现从具体的业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列的算法,并让这些算法可以相互替换。
策略模式的重心不是如何来实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
2.策略模式和if-else语句
多个if.elseif语句表达的就是一个平等的功能结构,你要么执行if,要么执行else,或者是elseif,这个时候,if块中的实现和else块中的实现从运行地位上来讲是平等的。而策略模式就是把各个平等的具体实现封装到单独的策略实现类了,然后通过上下文来与具体的策略类进行交互。因此多个if-else语句可以考虑使用策略模式。
3.算法的平等性
策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正是因为这个平等性,才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。所以可以这样描述这一系列策略算法:策略算法是相同行为的不同实现。
4.谁来选择具体的策略算法
在策略模式中,可以在两个地方来进行具体策略的选择。一个是在客户端,当使用上下文的时候,由客户端来选择具体的策略算法,然后把这个策略算法设置给上下文。前面的示例代码就是这种情况。还有一个是客户端不管,由上下文来选择具体的策略算法。
5.Strategy的实现方式
在前面的示例中,Strategy都是使用接口来定义的,这也是常见的实现方式。但是如果多个算法具有公共功能的话,可以把Strategy实现成为抽象类,然后把多个算法的公共功能实现到Strategy中。
6.运行时策略的唯一性
运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。
7.增加新的策略
在前面的代码示例中,体会到了策略模式中切换算法的方便,但是增加一个新的算法会怎样呢?其实很简单,策略模式可以让你很灵活地扩展新的算法。具体的做法是,先写一个策略算法类来实现新的要求,然后在客户端使用的时候指定使用新的策略算法类就可以了。
整个流程下来,除了客户端发生变化外,己有的上下文、策略接口定义和策略的己有实现,都不需要做任何的修改,可见能很方便地扩展新的策略算法。
8、策略模式的调用顺序示意图
策略模式的调用顺序,有两种常见的情况,一种如同前面的示例,具体如下:
(1)先是客户端来选择并创建具体的策略对象。
(2)然后客户端创建上下文。
(3)接下来客户端就可以调用上下文的方法来执行功能了,在调用的时候,从客户端传入算法需要的参数。
(4)上下文接到客户的调用请求,会把这个请求转发给它持有的Strategy
另外一种如下,这里不详细介绍了,具体可以参照《研磨设计模式数据阅读》
四、优缺点
1、优点
定义一系列算法
策略模式的功能就是定义一系列算法,实现让这些算法可以相互替换。所以会为这一系列算法定义公共的接口,以约束一系列算法要实现的功能。如果这一系列算法具有公共功能,可以把策略接口实现成为抽象类,把这些公共功能实现到父类中,对于这个问题,前面讲了三种处理方法,这里就不再哕嗦了。
避免多重条件语句
根据前面的示例会发现,策略模式的一系列策略算法是平等的,是可以互换的,写在一起就是通过if-else结构来组织,如果此时具体的算法实现中又有条件语句,就构成了多重条件语句,使用策略模式能避免这样的多重条件语句。
更好的扩展性
在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在使用策略的地方选择使用这个新的策略实现就可以了。
2、缺点
客户必须了解每种策略的不同
策略模式也有缺点,比如让客户端来选择具体使用哪一个策略,这就需要客户了解所有的策略,还要了解各种策略的功能和不同,这样才能做出正确的选择,而且这样也暴露了策略的具体实现。
增加了对象数目
由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
只适合扁平的算法结构
策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能嵌套使用。
对于出现需要嵌套使用多个算法的情况,比如折上折、折后返卷等业务的实现,需要组合或者是嵌套使用多个算法的情况,可以考虑使用装饰模式,或是变形的职责链,或是AOP等方式来实现。
五、关联设计模式
1、策略模式和状态模式
这两个模式从模式结构上看是一样的,但是实现的功能却是不一样的。状态模式是根据状态的变化来选择相应的行为,不同的状态对应不同的类,每个状态对应的类实现了该状态对应的功能,在实现功能的同时,还会维护状态数据的变化。这些实现状态对应的功能的类之间是不能相互替换的。策略模式是根据需要或者是客户端的要求来选择相应的实现类,各个实现类是平等的,是可以相互替换的。另外策略模式可以让客户端来选择需要使用的策略算法;而状态模式一般是由上下文,或者是在状态实现类里面来维护具体的状态数据,通常不由客户端来指定状态。
2、策略模式和模板方法模式
这两个模式经常组合使用。模板方法重在封装算法骨架;而策略模式重在分离并封装算法实现。
3、策略模式和享元模式
这两个模式可组合使用。策略模式分离并封装出一系列的策略算法对象,这些对象的功能通常都比较单一很多时候就是为了实现某个算法的功能而存在。因此,针对这一系列的、多个细粒度的对象,可以应用享元模式来节省资源,但前提是这些算法对象要被频繁地使用,如果偶尔用一次,就没有必要做成享元了。