⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 潘志的技术笔记 「志哥聊技术」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

一、背景介绍

一说起策略设计模式,相比大家都不陌生,在实际的业务开发中,合理的使用设计模式来编程,可以让代码阅读起来层次感更强,同时扩展性也会得到提升!

最近看同事的代码时候,学到了一个小技巧,在某些场景下合理的使用策略模式还是非常有用的,在此分享一下给大家。

二、代码实践

在介绍 SpringBoot 中如何实现策略设计模式之前,我们先简单的回顾一下策略模式的设计思路。

以编写一个简单的程序计算器,代码如下!

  • 首先,我们定义一个Operation接口,用于逻辑的计算
public interface Operation {

/**
* 执行计算
* @param a
* @param b
* @return
*/
int execute(int a, int b);
}
  • 接着,分别将四个if判断逻辑独立成一个模块,来单独处理
public class AddOperation implements Operation {

@Override
public int execute(int a, int b) {
return a + b;
}
}
publicclass SubOperation implements Operation {

@Override
public int execute(int a, int b) {
return a - b;
}
}
publicclass MultiOperation implements Operation {

@Override
public int execute(int a, int b) {
return a * b;
}
}
publicclass DivOperation implements Operation {

@Override
public int execute(int a, int b) {
return a / b;
}
}
  • 然后,创建一个工厂类,用于处理客户端传入的参数
public class OperatorFactory {

privatestatic Map<String, Operation> operationMap = new HashMap<>();

static {
//初始化实现类
operationMap.put("add", new AddOperation());
operationMap.put("sub", new SubOperation());
operationMap.put("multi", new MultiOperation());
operationMap.put("div", new DivOperation());
// more operators
}

/**
* 获取对应的目标实现类
* @param operator
* @return
*/
public static Optional<Operation> getOperation(String operator){
return Optional.ofNullable(operationMap.get(operator));
}

}
  • 最后,在需要的地方引入方法即可!
public class OperatorTestMain {

public static void main(String[] args) {
//获取计算的目标实现类
Operation targetOperation = OperatorFactory
.getOperation("add")
.orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
int result = targetOperation.execute(1, 2);
System.out.println("result:" + result);
}
}

以上就是一个典型的策略模式的实践思路,从代码阅读性、扩展性角度看,还是非常干净利落的。

那么,在SpringBoot项目中,我们应该如何使用呢?

三、SpringBoot 实践应用

3.1、方案一

  • 首先,我们还是定义一个Command接口,用于方法的抽象和统一
public interface Command {

/**
* 命令类型
* @return
*/
String operateType();

/**
* 执行
* @param a
* @param b
* @return
*/
Integer execute(int a, int b);

}
  • 接着,编写四套不同的计算处理逻辑
@Component
publicclass AddCommand implements Command {

@Override
public String operateType() {
return"add";
}

@Override
public Integer execute(int a, int b) {
return a + b;
}
}
@Component
publicclass SubCommand implements Command {

@Override
public String operateType() {
return"subtract";
}

@Override
public Integer execute(int a, int b) {
return a - b;
}
}
@Component
publicclass MultiCommand implements Command {

@Override
public String operateType() {
return"multiply";
}

@Override
public Integer execute(int a, int b) {
return a * b;
}
}
@Component
publicclass DivCommand implements Command {

@Override
public String operateType() {
return"divide";
}

@Override
public Integer execute(int a, int b) {
return a / b;
}
}
  • 然后,编写一个类似于上文的策略处理类
@Component
publicclass CalculatorService implements ApplicationContextAware {

private Map<String, Command> commandMap = new ConcurrentHashMap<>();


/**
* 执行计算
* @param operateType
* @param a
* @param b
* @return
*/
public int calculate(String operateType,int a, int b){
Command targetCommand = Optional.ofNullable(commandMap.get(operateType))
.orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
return targetCommand.execute(a,b);
}


@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Command> tempMap = applicationContext.getBeansOfType(Command.class);
tempMap.values().forEach(source -> commandMap.put(source.operateType(), source));
}

}
  • 最后,我们只需要在适当的位置应用即可!
@RunWith(SpringRunner.class)
@SpringBootTest
public class CalculatorServiceTest {

@Autowired
private CalculatorService calculatorService;

@Test
public void test(){
int result = calculatorService.calculate("add", 1,2);
System.out.println("result:" + result);
}
}

总结:这种方案的实践,和上面介绍的思路基本上一致,不同的地方在于,当 springboot 启动时,会将对象注入到IOC容器。

3.2、方案二(推荐)

翻查Spring的ioc容器,你会发现一个秘密,当一个接口有多个实现类时,Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类。

简单的说,我们只需要通过@Autowired注入对象,不需要通过CalculatorService这个类进行单独配置,操作方式如下!

  • 首先,编写一个CommandFactory工厂类,用于逻辑的处理
@Component
publicclass CommandFactory {

/**
* Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类
*/
@Autowired
private Map<String, Command> commandMap;


/**
* 执行计算
* @param operateType
* @param a
* @param b
* @return
*/
public int calculate(String operateType,int a, int b){
Command targetCommand = Optional.ofNullable(commandMap.get(operateType))
.orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
return targetCommand.execute(a,b);
}
}
  • 最后,直接在合适的地方使用CommandFactory即可!
@RunWith(SpringRunner.class)
@SpringBootTest
public class CalculatorServiceTest {

@Autowired
private CommandFactory commandFactory;

@Test
public void test(){
int result = commandFactory.calculate("addCommand", 1,2);
System.out.println("result:" + result);
}
}

总结:方案二和方案一的不同点在于,不需要显式的编写CalculatorService策略处理类来初始化对象,Spring在初始化对象的时候,可以帮忙我们实现对象的注入!

四、小结

本文主要围绕在 SpringBoot 引入策略模式的设计思路和实践方法进行介绍,在实际的业务开发中,合理的使用策略模式,能让代码看起来更佳清爽,业务扩展性也更佳强大,希望能帮助到大家!

文章目录
  1. 1. 一、背景介绍
  2. 2. 二、代码实践
  3. 3. 三、SpringBoot 实践应用
    1. 3.0.1. 3.1、方案一
    2. 3.0.2. 3.2、方案二(推荐)
  • 4. 四、小结