策略模式是一种行为模式,之所以放在工厂模式的后面来讲,是因为这个模式比较简单,而且和工厂模式混用起来很方便也很常用。
定义
- Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使他们可以互换)
优点
- 算法可以自由切换:只要实现了抽象策略,那么就成为了一个策略,通过封装角色对其封装,保证对外提供“可自由变换”的策略
- 避免多重条件判断:简单来说就是可以减少你代码里面的if else。
- 扩展良好:简单来说就是要增加一个if else的条件,只需要增加一个策略
缺点
- 策略类的数量多:每一个策略对应一个类,复用的可能小。
- 所有的策略类都要对外暴露:上层模块必须知道有那些策略,然后才能决定使用哪一个策略。不符合迪米特法则。(可以通过工厂模式,代理模式,享元模式来解决这个问题)
使用场景
- 需求:使用场景有多个
- 多个类只有在算法或行为上稍有不同的场景
- 算法需要自由切换的场景:如银行业务。
- 需要屏蔽算法规则的场景:只需要知道输入就会给你相应的输出。
代码
通用类图
基本的代码
类图
代码
策略模式基本的代码很简单,而且某种程度上你会发现你甚至用过这段代码。
策略接口类(接口)
1 2 3 4 5 6 7 8
| package com.pattern.strategy.template;
public interface Strategy { void doSomething(); }
|
具体策略类(继承自策略接口类)
1 2 3 4 5 6 7 8 9 10
| package com.pattern.strategy.template.impl;
import com.pattern.strategy.template.Strategy;
public class StrategyImpl1 implements Strategy { @Override public void doSomething() { System.out.println("执行了策略1"); } }
|
1 2 3 4 5 6 7 8 9 10
| package com.pattern.strategy.template.impl;
import com.pattern.strategy.template.Strategy;
public class StrategyImpl2 implements Strategy { @Override public void doSomething() { System.out.println("执行了策略2"); } }
|
封装类(类)
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.pattern.strategy.template;
public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public void doSomething(){ this.strategy.doSomething(); } }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.pattern;
import com.pattern.strategy.template.Context; import com.pattern.strategy.template.Strategy; import com.pattern.strategy.template.impl.StrategyImpl1; import org.junit.Test;
public class StrategyTest { @Test public void StrategyTemplateTest(){ Strategy strategy = new StrategyImpl1(); Context context = new Context(strategy); context.doSomething(); } }
|
结果
策略枚举
策略枚举是策略模式的一种扩展,为方便解释,这里采用加减法计算的场景。
代码
策略枚举代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.pattern.strategy.strategyenum;
public enum Calculator { ADD("+") { @Override public int exec(int a, int b) { return a + b; } }, SUB("-") { @Override public int exec(int a, int b) { return a - b; } }; public abstract int exec(int a, int b); private String value = ""; private Calculator(String value) { this.value = value; } public String getValue() { return value; } }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.pattern;
import com.pattern.strategy.strategyenum.Calculator; import com.pattern.strategy.template.Context; import com.pattern.strategy.template.Strategy; import com.pattern.strategy.template.impl.StrategyImpl1; import org.junit.Test;
public class StrategyTest { @Test public void StrategyEnumTest(){ System.out.println("执行加法运算:"); System.out.println("1+3 = " + Calculator.ADD.exec(1,3)); System.out.println("执行减法运算:"); System.out.println("5-3 = " + Calculator.SUB.exec(5,3)); } }
|
运行结果
注意
- 如果一个策略家族的具体策略数量超过了4个,就要考虑使用混合模式,来解决对外暴露的问题,否则会降低代码的可维护性。
- 策略枚举是一个优秀的模式,但是受枚举类型的限制,每个枚举都是public、final、static的,所以扩展性会有一定的约束,在实际开发中,策略枚举一般担当不经常发生变化的角色。