SpringBoot 使用策略+工厂模式解决大量 If Else 问题
文章目录
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。
系统环境:
- SpringBoot版本: 2.5.4
- JAVA JDK 版本: 1.8
示例地址:
一、Spring 接口中使用 If Else 的不足
在我们日常使用 Spring 框架,并且编写 Controller 代码时,实现传递一个参数从而执行订购不同产品,调用不同的 Service 逻辑,实现这种逻辑使用 If Else
实现是我们最常用的写法,写起来比较简单,代码如下:
1@RestController
2public class ProductController {
3
4 @Resource
5 private ServiceOrderA serviceOrderA;
6 @Resource
7 private ServiceOrderB serviceOrderB;
8
9 @PostMapping("/order")
10 public String order(@RequestParam(value = "type") String type) {
11 if ("productA".equals(type)) {
12 serviceOrderA.orderingProduct();
13 } else if ("productB".equals(type)) {
14 serviceOrderB.orderingProduct();
15 }
16 // 没有对应的产品,抛出异常并返回null
17 throw new RuntimeException("没有发现对应的产品处理策略");
18 return null;
19 }
20
21}
可以看到,如果是业务初期产品比较少,这时候使用 If Else
能很方便的实现。不过如果业务后面发展不错,新增了几百个产品,这个时候使用 If Else
时,代码如下:
1@RestController
2public class ProductController {
3
4 @Resource
5 private ServiceOrderA serviceOrderA;
6 @Resource
7 private ServiceOrderB serviceOrderB;
8 @Resource
9 private ServiceOrderC ServiceOrderC;
10 @Resource
11 private ServiceOrderD serviceOrderD
12 @Resource
13 private ServiceOrderE serviceOrderE;
14 @Resource
15 private ServiceOrderF serviceOrderF;
16 @Resource
17 private ServiceOrderG serviceOrderG;
18 ......(更多,略)
19
20 @PostMapping("/order")
21 public String order(@RequestParam(value = "type") String type) {
22 if ("productA".equals(type)) {
23 serviceOrderA.orderingProduct();
24 } else if ("productB".equals(type)) {
25 serviceOrderB.orderingProduct();
26 } else if ("productC".equals(type)) {
27 serviceOrderC.orderingProduct();
28 } else if ("productD".equals(type)) {
29 serviceOrderD.orderingProduct();
30 } else if ("productE".equals(type)) {
31 serviceOrderE.orderingProduct();
32 } else if ("productF".equals(type)) {
33 serviceOrderF.orderingProduct();
34 } else if ("productG".equals(type)) {
35 serviceOrderG.orderingProduct();
36 }
37 ......(更多,略)
38 // 没有对应的产品,抛出异常并返回null
39 throw new RuntimeException("没有发现对应的产品处理策略");
40 return null;
41 }
42
43}
可以看到,存在大量的 If Else
,让人一眼看下去有点密集恐惧症,并且不容易维护。
这时候就需要考虑使用设计模式,解决这种大量代码堆到一个方法中的情况。经过网上一番搜索,找到了大家常用于解决这种问题的方案,那就是使用 “策略模式” 与 “工厂模式” 两种结合,这种方式使用起来可以有效避免产生大量的 If Else
问题,使用后 Controller 代码如下:
1@RestController
2public class ProductController {
3
4 @Resource
5 private ProductStrategyFactory factoryForStrategy;
6
7 @PostMapping("/order")
8 public String order(@RequestParam(value = "type") String type) {
9 ProductService productService = factoryForStrategy.getProductStrategy(type);
10 return productService != null ? productService.orderingProduct() : "没有发现对应的产品处理策略";
11 }
12
13}
工厂类如下:
1@Component
2public class ProductStrategyFactory {
3
4 /**
5 * 使用依赖注入引入 ProductService 产品实现类,以 Bean 名称作为 Map 的 Key,以 Bean 实现类作为 Value
6 */
7 @Resource
8 private Map<String, ProductService> strategyMap = new ConcurrentHashMap<>(2);
9
10 /**
11 * 查找对应的产品的处理策略
12 *
13 * @param productName 产品名称
14 * @return 对应的产品订购逻辑实现策略
15 */
16 public ProductService getProductStrategy(String productName) {
17 return strategyMap.get(productName);
18 }
19
20}
二、Spring 工厂+策略模式的适用场景
使用 “工厂+策略模式” 比较适合通过某些参数,去执行不同的业务逻辑的这种场景,能很好的解决执行逻辑相同,调用上游接口逻辑不同这种情形。
三、Spring 工厂+策略模式的示例项目
Maven 引入 Springfox 依赖
新建 Maven 项目,在 pom.xml 文件中引入 SpringBoot 依赖。
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5
6 <parent>
7 <groupId>org.springframework.boot</groupId>
8 <artifactId>spring-boot-starter-parent</artifactId>
9 <version>2.5.4</version>
10 </parent>
11
12 <groupId>club.mydlq</groupId>
13 <artifactId>springboot-strategy-factory-pattern</artifactId>
14 <version>1.0.0</version>
15 <name>springboot-strategy-factory-pattern</name>
16 <description>strategy factory pattern</description>
17
18 <properties>
19 <java.version>1.8</java.version>
20 </properties>
21
22 <dependencies>
23 <dependency>
24 <groupId>org.springframework.boot</groupId>
25 <artifactId>spring-boot-starter-web</artifactId>
26 </dependency>
27 </dependencies>
28
29 <build>
30 <plugins>
31 <plugin>
32 <groupId>org.springframework.boot</groupId>
33 <artifactId>spring-boot-maven-plugin</artifactId>
34 </plugin>
35 </plugins>
36 </build>
37
38</project>
创建产品接口类 (用于定义订购方法的策略接口类)
创建产品的接口类,该类就相当于策略模式中的接口类,里面定义一个策略的方法定义,子类会实现该策略方法。不过不同的是,这里接口类中定义的一种产品订购的方法,不同的子类来实现不同产品订购的方法。
1public interface ProductService {
2
3 /**
4 * 订购产品
5 *
6 * @return 订购信息
7 */
8 String orderingProduct();
9
10}
创建接口的实现类 (实现不同订购策略的类)
这里创建两个接口的实现类,每个实现类都各自实现自己的订购下单的逻辑。
产品 A 实现类
1import mydlq.club.example.service.ProductService;
2import org.springframework.stereotype.Service;
3
4@Service("productA")
5public class ProductFirstServiceImpl implements ProductService {
6
7 @Override
8 public String orderingProduct() {
9 // 执行产品订购逻辑
10 //....
11 return "成功订购产品A";
12 }
13
14}
产品 B 实现类
1import mydlq.club.example.service.ProductService;
2import org.springframework.stereotype.Service;
3
4@Service("productB")
5public class ProductSecondServiceImpl implements ProductService {
6
7 @Override
8 public String orderingProduct() {
9 // 执行产品订购逻辑
10 //....
11 return "成功订购产品B";
12 }
13
14}
创建工厂类
1import java.util.Map;
2import javax.annotation.Resource;
3import org.springframework.stereotype.Component;
4import java.util.concurrent.ConcurrentHashMap;
5import mydlq.club.example.service.ProductService;
6
7@Component
8public class ProductStrategyFactory {
9
10 /**
11 * 使用依赖注入引入 ProductService 产品实现类,以 Bean 名称作为 Map 的 Key,以 Bean 实现类作为 Value
12 */
13 @Resource
14 private Map<String, ProductService> strategyMap = new ConcurrentHashMap<>(2);
15
16 /**
17 * 查找对应的产品的处理策略
18 *
19 * @param productName 产品名称
20 * @return 对应的产品订购逻辑实现策略
21 */
22 public ProductService getProductStrategy(String productName) {
23 // 根据从 productName 从 strategyMap 集合中查询对应的产品下单策略
24 return strategyMap.get(productName);
25 }
26
27}
创建测试的 Controller 类
创建用于测试的接口,里面实现了接收不同的产品类型,去工厂里获取不同的产品下单实现类,然后调用不同的产品下单实现类去执行下单订购的业务逻辑。
1import mydlq.club.example.service.ProductService;
2import mydlq.club.example.service.factory.ProductStrategyFactory;
3import org.springframework.web.bind.annotation.*;
4import javax.annotation.Resource;
5
6@RestController
7public class ProductController {
8
9 @Resource
10 private ProductStrategyFactory factoryForStrategy;
11
12 /**
13 * 执行下单订购产品
14 *
15 * @param type 产品类型(策略)
16 * @return 订购结果
17 */
18 @PostMapping("/order")
19 public String order(@RequestParam(value = "type") String type) {
20 ProductService productService = factoryForStrategy.getProductStrategy(type);
21 return productService != null ? productService.orderingProduct() : "没有发现对应的产品处理策略";
22 }
23
24}
创建 SpringBoot 启动类
创建启动类,用于启动 SpringBoot 应用。
1import org.springframework.boot.SpringApplication;
2import org.springframework.boot.autoconfigure.SpringBootApplication;
3
4@SpringBootApplication
5public class Application {
6
7 public static void main(String[] args) {
8 SpringApplication.run(Application.class, args);
9 }
10
11}
访问示例项目进行测试
本地启动项目访问进行测试,观察到的效果如下:
(1) 设置参数 type=productA
,则响应的内容如下:
1$ curl -XPOST http://localhost:8080/order?type=productA
2
3成功订购产品A
(2) 设置参数 type=productB
,则响应的内容如下:
1$ curl -XPOST http://localhost:8080/order?type=productB
2
3成功订购产品B
(3) 设置参数 type=productC
,该产品并不存在,返回的响应内容如下:
1$ curl -XPOST http://localhost:8080/order?type=productC
2
3没有发现对应的产品处理策略
四、最后的总结
使用 “工厂+策略模式” 可以解决大量 If Else
问题,不过使用哪种方案从来都是根据实际情况考虑,如果你只有几种产品,且产品数量变化不大,使用 If Else
比使用 “工厂+策略模式” 更加简单,更加方便开发人员实现业务代码逻辑。
---END---
如果本文对你有帮助,可以关注我的公众号"小豆丁技术栈"了解最新动态,顺便也请帮忙 github 点颗星哦~感谢~
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。