转载自:http://blog.csdn.net/goskalrie/article/details/52472734
简介
定义
策略模式:将可变的部分从程序中抽象分理出算法接口,在该接口下分别封装一系列算法实现,并使他们可以相互替换,从而导致客户端的程序独立于算法的改变。
策略模式中的设计原则:变化的抽象成接口;面向接口编程而不是面向实现编程。
原理
将有共性的行为抽象为一个接口,其中有此种行为的声明。该接口的实现类都实现了这个行为,但是提供了不同的行为表现,也就是说都是支付,有用网银的,也有用支付宝的。在执行此类行为的类中含有该接口的引用,在执行行为前要确定该行为的表现,选择适合的执行。
策略模式的简单实现
生活中讲到策略,首先想到的是三十六计。有商业三十六计、爱情三十六计、把妹三十六计……下面以某帅哥把妹为例,实现一个简单的策略模式的应用。话说,一帅哥,从事软件编程行业,成为程序猿家族中的一员,一日,他看到一妹子,清纯可口,使之春心怒放,妹子坚守阵地,奈何此猿熟谙三十六计,最终将此妹子俘获:
- /**
- * 抽象策略
- * @author Goser (mailto:goskalrie@163.com)
- * @Since 2016年9月8日
- */
- public interface ThirtySixStrategy {
- void useStrategy();
- }
- /**
- * 环境角色类
- * @author Goser (mailto:goskalrie@163.com)
- * @Since 2016年9月8日
- */
- public class Handsome {
- private ThirtySixStrategy tss; //含有对抽象策略接口的引用。
- public Handsome(ThirtySixStrategy tss){
- this.tss = tss;
- }
- public void getTheGirl(){
- tss.useStrategy();
- }
- }
- /** 连环计:具体策略*/
- public class Chain implements ThirtySixStrategy {
- public void useStrategy() {
- System.out.println("程序猿使出了* 连环计 *对程序媛进行地毯式轰炸,俘获了程序媛。。。");
- }
- }
- /**欲擒故纵:具体策略*/
- public class Loose2Capture implements ThirtySixStrategy {
- public void useStrategy() {
- System.out.println("程序猿使出了* 欲擒故纵 *计俘获了程序媛。。。");
- }
- }
- /**美男计:具体策略*/
- public class HandsomeGuy implements ThirtySixStrategy {
- public void useStrategy() {
- System.out.println("程序猿使出了* 美男计 *计俘获了程序媛。。。");
- }
- }
- /**测试类*/
- public class Test {
- public static void main(String[] args) {
- ThirtySixStrategy tssHsg = new HandsomeGuy();
- Handsome guy = new Handsome(tssHsg);
- guy.getTheGirl();
- ThirtySixStrategy tssCh = new Chain();
- guy = new Handsome(tssCh);
- guy.getTheGirl();
- }
- }
输出:
程序猿使出了* 美男计 *计俘获了程序媛。。。
程序猿使出了* 连环计 *对程序媛进行地毯式轰炸,俘获了程序媛。。。
可以发现程序猿的目的是把得妹子,途径有n多种,不同的程序猿会使用不同的方法实现该操作,那么如果把每一个程序猿把妹的方法都放到程序猿类中或是放到子类中,那么穷尽这些方法后,类将会相当庞大,几千行代码都不止。而使用策略模式,虽说需要增加一个策略抽象接口的实现类,但是单元化的优点便是重构易维护。然后告诉程序猿“喂,我给了你一份把妹策略菜单,你看看要用哪一个”。
Spring中的策略模式
看一下Spring中策略模式的使用,以Spring实例化对象时使用的策略模式为例,该部分源码位于org.objenesis.strategy下,看名字就很直白,对象(obj)实例化(enesis).策略(strategy):
1.策略接口
- /** 抽象策略接口:定义一个实例化指定类的最优策略*/
- public interface InstantiatorStrategy {
- /**
- *创建类的实例策略方法
- *
- * @param type 待实例化的类
- * @return实例化方法
- */
- <T> ObjectInstantiator<T>newInstantiatorOf(Class<T> type);
- }
2.具体策略
- public class SingleInstantiatorStrategy implements InstantiatorStrategy {
- private Constructor<?> constructor;
- public SingleInstantiatorStrategy(Class<?> instantiator) {
- try {
- constructor =instantiator.getConstructor(Class.class);
- } catch (NoSuchMethodException e) {
- throw new ObjenesisException(e);
- }
- }
- @SuppressWarnings("unchecked")
- public <T> ObjectInstantiator<T> newInstantiatorOf(Class<T>type) {
- try {
- return (ObjectInstantiator<T>) constructor.newInstance(type);
- } catch (InstantiationException e) {
- throw new ObjenesisException(e);
- } catch (IllegalAccessException e) {
- throw new ObjenesisException(e);
- } catch (InvocationTargetException e) {
- throw new ObjenesisException(e);
- }
- }
- }
3.环境角色,拥有策略接口的引用,使用具体策略
- public class ObjenesisBase implements Objenesis {
- protected finalInstantiatorStrategy strategy;
- protected ConcurrentHashMap<String,ObjectInstantiator<?>> cache;
- //…其他代码
- public ObjenesisBase(InstantiatorStrategy strategy) {
- this(strategy, true);
- public <T> T newInstance(Class<T> clazz) {
- return getInstantiatorOf(clazz).newInstance();
- }
- }
Spring中的策略模式和我们把妹的策略模式差不多,但是加入了更多的功能,使用了更复杂的技术,抽象类、继承、泛型……
总结
实现步骤
通过上面的两部分代码可以总结策略模式的实现步骤:
1. 通过分离变化得出的策略接口Strategy
2. Strategy的实现类
3. 客户程序有一个Strategy
4. 在客户程序中选择/组装正确的Strategy实现
适用场景
1. 许多相关的类仅仅是行为差异
2. 运行时选取不同的算法变体
3. 通过条件语句在多个分支中选取一
策略模式的优缺点
优点:
(1) 提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公用的代码移到父类里面,从而可以避免重复的代码。
(2) 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不能再独立烟花。继承使得动态改变算法或行为变得不可能。
(3) 使用策略模式可以避免使用多重条件转移语句。
缺点:
(1) 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。
(2) 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。