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

timo-nbktp 1年前 ⋅ 1550 阅读

 

在我们日常使用 Spring 框架,并且编写 Controller 代码时,实现传递一个参数从而执行订购不同产品,调用不同的 Service 逻辑,实现这种逻辑使用 If Else 实现是我们最常用的写法,写起来比较简单,代码如下:

@RestController
public class ProductController {

    @Resource
    private ServiceOrderA serviceOrderA;
    @Resource
    private ServiceOrderB serviceOrderB;

    @PostMapping("/order")
    public String order(@RequestParam(value = "type") String type) {
        if ("productA".equals(type)) {
            serviceOrderA.orderingProduct();
        } else if ("productB".equals(type)) {
            serviceOrderB.orderingProduct();  
        }
        // 没有对应的产品,抛出异常并返回null
        throw new RuntimeException("没有发现对应的产品处理策略");
        return null;
    }

}

可以看到,如果是业务初期产品比较少,这时候使用 If Else 能很方便的实现。不过如果业务后面发展不错,新增了几百个产品,这个时候使用 If Else 时,代码如下:

@RestController
public class ProductController {

    @Resource
    private ServiceOrderA serviceOrderA;
    @Resource
    private ServiceOrderB serviceOrderB;
    @Resource
    private ServiceOrderC ServiceOrderC;
    @Resource
    private ServiceOrderD serviceOrderD
    @Resource
    private ServiceOrderE serviceOrderE;
    @Resource
    private ServiceOrderF serviceOrderF;
    @Resource
    private ServiceOrderG serviceOrderG;
    ......(更多,略)

    @PostMapping("/order")
    public String order(@RequestParam(value = "type") String type) {
        if ("productA".equals(type)) {
            serviceOrderA.orderingProduct();
        } else if ("productB".equals(type)) {
            serviceOrderB.orderingProduct();  
        } else if ("productC".equals(type)) {
            serviceOrderC.orderingProduct();  
        } else if ("productD".equals(type)) {
            serviceOrderD.orderingProduct();  
        } else if ("productE".equals(type)) {
            serviceOrderE.orderingProduct();  
        } else if ("productF".equals(type)) {
            serviceOrderF.orderingProduct();  
        } else if ("productG".equals(type)) {
            serviceOrderG.orderingProduct();  
        }
        ......(更多,略)
        // 没有对应的产品,抛出异常并返回null
        throw new RuntimeException("没有发现对应的产品处理策略");
        return null;
    }

}

可以看到,存在大量的 If Else,让人一眼看下去有点密集恐惧症,并且不容易维护。

这时候就需要考虑使用设计模式,解决这种大量代码堆到一个方法中的情况。经过网上一番搜索,找到了大家常用于解决这种问题的方案,那就是使用 “策略模式”“工厂模式” 两种结合,这种方式使用起来可以有效避免产生大量的 If Else 问题,使用后 Controller 代码如下:

@RestController
public class ProductController {

    @Resource
    private ProductStrategyFactory factoryForStrategy;

    @PostMapping("/order")
    public String order(@RequestParam(value = "type") String type) {
        ProductService productService = factoryForStrategy.getProductStrategy(type);
        return productService != null ? productService.orderingProduct() : "没有发现对应的产品处理策略";
    }

}

工厂类如下:

@Component
public class ProductStrategyFactory {

   /**
    * 使用依赖注入引入 ProductService 产品实现类,以 Bean 名称作为 Map 的 Key,以 Bean 实现类作为 Value
    */
    @Resource
    private Map<String, ProductService> strategyMap = new ConcurrentHashMap<>(2);

  /**
     * 查找对应的产品的处理策略
     *
     * @param productName 产品名称
     * @return 对应的产品订购逻辑实现策略
     */
    public ProductService getProductStrategy(String productName) {
        return strategyMap.get(productName);
    }

}

使用 “工厂+策略模式” 比较适合通过某些参数,去执行不同的业务逻辑的这种场景,能很好的解决执行逻辑相同,调用上游接口逻辑不同这种情形。

新建 Maven 项目,在 pom.xml 文件中引入 SpringBoot 依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
    </parent>

    <groupId>club.mydlq</groupId>
    <artifactId>springboot-strategy-factory-pattern</artifactId>
    <version>1.0.0</version>
    <name>springboot-strategy-factory-pattern</name>
    <description>strategy factory pattern</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

创建产品的接口类,该类就相当于策略模式中的接口类,里面定义一个策略的方法定义,子类会实现该策略方法。不过不同的是,这里接口类中定义的一种产品订购的方法,不同的子类来实现不同产品订购的方法。

public interface ProductService {

    /**
     * 订购产品
     *
     * @return 订购信息
     */
    String orderingProduct();

}

这里创建两个接口的实现类,每个实现类都各自实现自己的订购下单的逻辑。

产品 A 实现类

import mydlq.club.example.service.ProductService;
import org.springframework.stereotype.Service;

@Service("productA")
public class ProductFirstServiceImpl implements ProductService {

    @Override
    public String orderingProduct() {
        // 执行产品订购逻辑
        //....
        return "成功订购产品A";
    }

}

产品 B 实现类

import mydlq.club.example.service.ProductService;
import org.springframework.stereotype.Service;

@Service("productB")
public class ProductSecondServiceImpl implements ProductService {

    @Override
    public String orderingProduct() {
        // 执行产品订购逻辑
        //....
        return "成功订购产品B";
    }

}
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import mydlq.club.example.service.ProductService;

@Component
public class ProductStrategyFactory {

   /**
      * 使用依赖注入引入 ProductService 产品实现类,以 Bean 名称作为 Map 的 Key,以 Bean 实现类作为 Value
      */
    @Resource
    private Map<String, ProductService> strategyMap = new ConcurrentHashMap<>(2);

  /**
     * 查找对应的产品的处理策略
     *
     * @param productName 产品名称
     * @return 对应的产品订购逻辑实现策略
     */
    public ProductService getProductStrategy(String productName) {
        // 根据从 productName 从 strategyMap 集合中查询对应的产品下单策略
        return strategyMap.get(productName);
    }

}

创建用于测试的接口,里面实现了接收不同的产品类型,去工厂里获取不同的产品下单实现类,然后调用不同的产品下单实现类去执行下单订购的业务逻辑。

import mydlq.club.example.service.ProductService;
import mydlq.club.example.service.factory.ProductStrategyFactory;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;

@RestController
public class ProductController {

    @Resource
    private ProductStrategyFactory factoryForStrategy;

    /**
     * 执行下单订购产品
     *
     * @param type 产品类型(策略)
     * @return 订购结果
     */
    @PostMapping("/order")
    public String order(@RequestParam(value = "type") String type) {
        ProductService productService = factoryForStrategy.getProductStrategy(type);
        return productService != null ? productService.orderingProduct() : "没有发现对应的产品处理策略";
    }

}

创建启动类,用于启动 SpringBoot 应用。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

本地启动项目访问进行测试,观察到的效果如下:

(1) 设置参数 type=productA,则响应的内容如下:

$ curl -XPOST http://localhost:8080/order?type=productA

成功订购产品A

(2) 设置参数 type=productB,则响应的内容如下:

$ curl -XPOST http://localhost:8080/order?type=productB

成功订购产品B

(3) 设置参数 type=productC,该产品并不存在,返回的响应内容如下:

$ curl -XPOST http://localhost:8080/order?type=productC

没有发现对应的产品处理策略

使用 “工厂+策略模式” 可以解决大量 If Else 问题,不过使用哪种方案从来都是根据实际情况考虑,如果你只有几种产品,且产品数量变化不大,使用 If Else 比使用 “工厂+策略模式” 更加简单,更加方便开发人员实现业务代码逻辑。

 

---end---

 

 

版权 本着开源共享、共同学习的精神,本文转载自 http://www.mydlq.club/article/119/#documentTop , 如果侵权之处,请联系博主进行删除,谢谢~