在讲策略模式之前,我们先举个例子
假设现在要实现一个计算器程序,如果让你来设计,你会不会这么写?校验不是重点所以忽略
package cn.limingcheng.strategy;
import java.math.BigDecimal;
public class Calculator {
/**
* 两个数加减乘除操作
*
* @param val1 第一个数字
* @param val2 第二个数字
* @param calcType 计算方式
* @return
*/
public BigDecimal calc(BigDecimal val1, BigDecimal val2, String calcType) {
if ("+".equals(calcType)) {
return val1.add(val2);
} else if ("-".equals(calcType)) {
return val1.subtract(val2);
} else if ("*".equals(calcType)) {
return val1.multiply(val2);
} else if ("/".equals(calcType)) {
return val1.divide(val2);
}
return null;
}
}
代码可以很稳定的运行,但是如果现在增加求两个数余数怎么办?再增加个if else?很明显不符合开闭原则,修改原来的代码可能会造成想不到的结果,所以先做个小改进,将算法剥离出来
package cn.limingcheng.strategy;
import java.math.BigDecimal;
public class Calculator {
/**
* 两个数加减乘除操作
*
* @param val1 第一个数字
* @param val2 第二个数字
* @param calcType 计算方式
* @return
*/
public BigDecimal calc(BigDecimal val1, BigDecimal val2, String calcType) {
if ("+".equals(calcType)) {
return add(val1, val2);
} else if ("-".equals(calcType)) {
return subtract(val1, val2);
} else if ("*".equals(calcType)) {
return multiply(val1, val2);
} else if ("/".equals(calcType)) {
return divide(val1, val2);
}
return null;
}
/**
* 加法
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
private BigDecimal add(BigDecimal val1, BigDecimal val2) {
return val1.add(val2);
}
/**
* 减法
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
private BigDecimal subtract(BigDecimal val1, BigDecimal val2) {
return val1.subtract(val2);
}
/**
* 乘法
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
private BigDecimal multiply(BigDecimal val1, BigDecimal val2) {
return val1.multiply(val2);
}
/**
* 除法
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
private BigDecimal divide(BigDecimal val1, BigDecimal val2) {
return val1.divide(val2);
}
}
上面代码相比第一种将算法剥离出来,当只有一个算法改动时只需要修改相应的即可,不过问题依旧存在,当增加求余数方法的时候还要修改原来的类,这时策略模式闪亮登场
第一步定义策略接口角色
package cn.limingcheng.strategy;
/**
* 策略接口
*/
public interface IStrategy {
/**
* 定义抽象方法约束具体算法的实现
*/
public void algorithmInterface();
}
第二步编写具体策略角色,我这边定义两个:ConcreteStrategyA 和 ConcreteStrategyB
package cn.limingcheng.strategy;
/**
* 策略的具体实现A
*/
public class ConcreteStrategyA implements IStrategy {
/**
* 具体的算法实实现
*/
@Override
public void algorithmInterface() {
System.out.println("ConcreteStrategyA method");
}
}
package cn.limingcheng.strategy;
/**
* 策略的具体实现B
*/
public class ConcreteStrategyB implements IStrategy {
/**
* 具体的算法实实现
*/
@Override
public void algorithmInterface() {
System.out.println("ConcreteStrategyB method");
}
}
第三步定义上下文角色
package cn.limingcheng.strategy;
/**
* 策略上下文
*/
public class Context {
/**
* 面向接口编程,持有策略接口
*/
private IStrategy strategy;
/**
* 通过构造器注入一个具体的策略实现类
*
* @param strategy 具体的策略
*/
public Context(IStrategy strategy) {
this.strategy = strategy;
}
/**
* 调用策略实现的方法
*/
public void call() {
strategy.algorithmInterface();
}
}
最后看成果吧,创建一个客户端程序测试
package cn.limingcheng.strategy;
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 实例化具体要使用的策略对象
IStrategy strategy = new ConcreteStrategyA();
// 创建策略上下文并且传入具体策略对象
Context context = new Context(strategy);
// 调用上下文提供的方法来完成对具体策略实现的回调
context.call();
}
}
先创建计算器策略接口
package cn.limingcheng.strategy.calc;
import java.math.BigDecimal;
/**
* 计算器策略接口
*/
public interface ICalculatorStrategy {
/**
* 两个数加减乘除操作
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
public BigDecimal calc(BigDecimal val1, BigDecimal val2);
}
实现具体策略,为了篇幅这边就写加法以及减法的实现类
package cn.limingcheng.strategy.calc;
import java.math.BigDecimal;
/**
* 加法策略实现类
*/
public class AddCalculatorStrategy implements ICalculatorStrategy {
/**
* 实现加法算法
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
@Override
public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
return val1.add(val2);
}
}
package cn.limingcheng.strategy.calc;
import java.math.BigDecimal;
/**
* 减法策略实现类
*/
public class SubtractCalculatorStrategy implements ICalculatorStrategy {
/**
* 实现减法算法
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
@Override
public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
return val1.subtract(val2);
}
}
定义计算器策略上下文
package cn.limingcheng.strategy.calc;
import java.math.BigDecimal;
/**
* 计算器策略上下文
*/
public class CalculatorContext {
/**
* 持有计算器策略接口引用
*/
private ICalculatorStrategy calculatorStrategy;
/**
* 通过构造器注入具体计算器策略对象
*
* @param calculatorStrategy
*/
public CalculatorContext(ICalculatorStrategy calculatorStrategy) {
this.calculatorStrategy = calculatorStrategy;
}
/**
* 对外提供方法实现对具体策略的方法调用
*
* @param val1
* @param val2
* @return
*/
public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
return calculatorStrategy.calc(val1, val2);
}
}
创建客户端进行测试
package cn.limingcheng.strategy.calc;
import java.math.BigDecimal;
/**
* 计算器客户端
*/
public class CalculatorClient {
public static void main(String[] args) {
// 实例化加法策略
ICalculatorStrategy add = new AddCalculatorStrategy();
// 实例化计算器策略上下文
CalculatorContext calculatorContext = new CalculatorContext(add);
// 调用具体策略算法
BigDecimal result = calculatorContext.calc(new BigDecimal("10"), new BigDecimal(20));
System.out.println(result);
}
}
使用策略模式改造计算器代码后,试想如果想增加求余数的策略怎么办?我们只需要增加一个求余数的策略实现类
package cn.limingcheng.strategy.calc;
import java.math.BigDecimal;
/**
* 余数策略实现类
*/
public class RemainderCalculatorStrategy implements ICalculatorStrategy {
/**
* 实现余数算法
*
* @param val1 第一个数字
* @param val2 第二个数字
* @return
*/
@Override
public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
return val1.remainder(val2);
}
}
客户端指定策略为余数策略即可
package cn.limingcheng.strategy.calc;
import java.math.BigDecimal;
/**
* 余数客户端
*/
public class RemainderCalculatorClient {
public static void main(String[] args) {
// 实例化余数策略
ICalculatorStrategy remainder = new RemainderCalculatorStrategy();
// 实例化计算器策略上下文
CalculatorContext remainderCalculatorContext = new CalculatorContext(remainder);
// 调用具体策略算法
BigDecimal remaindeResult = remainderCalculatorContext.calc(new BigDecimal("10"), new BigDecimal(3));
System.out.println(remaindeResult);
}
}
代码已上传至码云,传送门