目录
背景
在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、
骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。
在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。
如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,
不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。
策略模式
策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。策略模式使得算法或逻辑可以在不影响到客户端的情况下发生变化。说到策略模式就不得不提及OCP(Open Closed Principle) 开闭原则,即对扩展开放,对修改关闭。策略模式的出现很好地诠释了开闭原则,有效地减少了分支语句。
应用场景
- 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
- 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
- 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
优点
- 扩展性良好;
- 避免使用多重条件判断,遵循开闭原则;
- 算法可以自由切换。
缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类;
- 策略模式将造成产生很多策略类。
模式的结构
Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。
在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,
也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中
定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。
编写步骤
- 定义抽象策略角色(为策略对象定义一个公共的接口);
- 编写具体策略角色(算法实现);
- 定义环境,内部持有一个策略类的引用;
案例
需求:需要计算2个数的结果为多少,至于这2个数怎么算不关心(加法、减法、乘法、除法等),只关心结果。
定义抽象策略角色:
public interface Strategy {
/**
* 用于计算
*
* @param d1
* @param d2
* @return
*/
double calculate(double d1, double d2);
具体策略角色:
public class AddStrategy implements Strategy {
/**
* 用于计算(加法)
*
* @param d1
* @param d2
* @return
*/
@Override
public double calculate(double d1, double d2) {
return d1 + d2;
}
public class SubtractStrategy implements Strategy {
/**
* 用于计算(减法)
*
* @param d1
* @param d2
* @return
*/
@Override
public double calculate(double d1, double d2) {
return d1 - d2;
}
public class MultiplyStrategy implements Strategy {
/**
* 用于计算(乘法)
*
* @param d1
* @param d2
* @return
*/
@Override
public double calculate(double d1, double d2) {
return d1 * d2;
}
public class DivideStrategy implements Strategy {
/**
* 用于计算(除法)
*
* @param d1
* @param d2
* @return
*/
@Override
public double calculate(double d1, double d2) {
return d1 / d2;
}
定义环境(策略调用类):
public class StrategyContext {
//持有策略类的引用
private Strategy strategy;
public StrategyContext(Strategy strategy) {
this.strategy = strategy;
}
public double calculate(double d1, double d2) {
return strategy.calculate(d1, d2);
}
unit测试:
@Test
public void strategyTest(){
StrategyContext strategy1 = new StrategyContext(new AddStrategy());
System.out.println(strategy1.calculate(3,2));
StrategyContext strategy2 = new StrategyContext(new SubtractStrategy());
System.out.println(strategy2.calculate(3,2));
StrategyContext strategy3 = new StrategyContext(new MultiplyStrategy());
System.out.println(strategy3.calculate(3,2));
StrategyContext strategy4 = new StrategyContext(new DivideStrategy());
System.out.println(strategy4.calculate(3,2));
}
结果:
总结
比如我们扩展一个新的算法(求余),那么只需要在接口新增一个求余类去实现Strategy接口就可以,而不需要改动其他代码,策略模式遵循了开闭原则,这个就是策略模式的好处。