策略模式示例
2022年10月22日
策略模式示例
在写Java项目的业务逻辑中经常会遇到在一个业务代码块中有很多个分支的情况,且很多个分支中每条分支的执行逻辑又比较复杂,传统的方法是使用嵌套的if,else或者switch进行处理,但是这样处理的缺点是业务代码耦合度高,增加分支或者修改其中一个分支的逻辑,对原有的代码改动量大,而且代码易读性差,不优雅。
使用策略模式可以很好的避免以上情况,它定义了算法家族 分别封装起来它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式结构图
- Strategy 定义所有支持的算法的公共接口
- ConcreteStrategy, 封装了具体的算法或行为,继承于 Strategy
- Context, 用一个 ContextStrategy 来配置,维护 个对 Strategy 对象的引用
策略模式实现
策略模式需要基于具体的业务场景,以执行算法为例,系统内置了matlab算法,python算法和Java算法,当调用一个计算方法时根据传入的参数执行特定的算法并返回计算结果。
定义接口
定义所有支持的算法的公共接口
/**
* 执行算法的接口
*/
public interface ICalcExecService {
/**
* 执行算法
* @param algorithmName 算法文件名称
* @param data 算法入参数
* @return 算法执行结果
*/
public String execCalc(String algorithmName, double [][] data);
/**
* 执行python算法
* @param algorithmName 算法文件名称
* @param data 算法入参数
* @return 算法执行结果
*/
public String execCalc( String algorithmName,double [][] data,String [] params);
}
定义算法常量
/**
* 计算常量类
*/
public class CalcConstant {
/**算法类型java*/
public static final String JAVA = "Java";
/**算法类型python*/
public static final String PYTHON = "Python";
/**算法类型python*/
public static final String MATLAB = "Matlab";
}
定义策略工厂
用一个 ContextStrategy 来配置,维护 个对 Strategy 对象的引用
/**
* 策略工厂,实例化策略类
*/
@Service
public class StrategyFactory {
private StrategyFactory() {
}
private static final Map<String, ICalcExecService> strategyMap = new HashMap<>();
/*
注册策略
*/
public static void registerStrategy(String operation, ICalcExecService calcExecService) {
strategyMap.put(operation, calcExecService);
}
/*
获取策略实例
*/
public static ICalcExecService getStrategy(String operation) {
return strategyMap.get(operation);
}
}
定义策略实现类
封装了具体的算法或行为,继承于 Strategy。
/**
* java执行计算的实现类
*/
@Service
@Slf4j
public class CalcExecJavaService implements ICalcExecService, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.registerStrategy(CalcConstant.JAVA, this);
}
/**
* 执行java算法
* @param algorithmName 算法文件名称
* @param data 算法入参数据
* @return 算法执行结果
*/
@Override
public String execCalc(String algorithmName, double [][] data) {
return execCalc(algorithmName,data,null);
}
/**
* 执行java算法
* @param algorithmName 算法文件名称
* @param data 算法入参数据
* @return 算法执行结果
*/
@Override
public String execCalc( String algorithmName,double [][] data,String [] params) {
log.info("开始执行JAVA算法文件名称为:{}",algorithmName);
//todo: 具体的算法执行逻辑,result
return result;
}
}
/**
* python执行计算的实现类
*/
@Service
@Slf4j
public class CalcExecPythonService implements ICalcExecService, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.registerStrategy(CalcConstant.PYTHON, this);
}
/**
* 执行python算法
* @param algorithmName 算法文件名称
* @param data 算法入参数据
* @return 算法执行结果
*/
@Override
public String execCalc(String algorithmName, double [][] data) {
return execCalc(algorithmName,data,null);
}
/**
* 执行python算法
* @param algorithmName 算法文件名称
* @param data 算法入参数据
* @param params 算法自定义参数
* @return 算法执行结果
*/
@Override
public String execCalc( String algorithmName,double [][] data,String [] params) {
log.info("开始执行PYTHON算法文件名称为:{}",algorithmName);
//todo: 具体的算法执行逻辑,result
return result;
}
}
调用算法
//根据传入的算法类型,调用工厂方法获取策略实例
ICalcExecService calcExecService = StrategyFactory.getStrategy(CalcConstant.PYTHON);
//传入算法名称和入参,执行计算方法获取返回结果
String result = calcExecService.execCalc(algorithmName, data);
总结
以上示例代码完美的实现了策略模式,通过策略工厂可以在容器加载的时候就将策略实现类注册到context中,当需要使用时根据执行算法的类型获取策略实例进行算法执行,不同算法之间完全解耦,如果新增算法则只需要新增算法策略的实例,不会对原有业务代码产生冲击,更优雅更易读。