SpringBoot 使用策略+工厂模式解决大量 If Else 问题

SpringBoot 使用策略+工厂模式解决大量 If Else 问题

文章目录

  !版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。


系统环境:

  • SpingBoot版本: 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 点颗星哦~感谢~


  !版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。