SpringBoot 使用策略+工厂模式解决大量 If Else 问题
系统环境:
- SpringBoot版本: 2.5.4
- JAVA JDK 版本: 1.8
示例地址:
一、Spring 接口中使用 If Else 的不足
在我们日常使用 Spring 框架,并且编写 Controller 代码时,实现传递一个参数从而执行订购不同产品,调用不同的 Service 逻辑,实现这种逻辑使用 If Else 实现是我们最常用的写法,写起来比较简单,代码如下:
@RestControllerpublic 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 时,代码如下:
@RestControllerpublic 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 代码如下:
@RestControllerpublic 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() : "没有发现对应的产品处理策略"; }
}工厂类如下:
@Componentpublic 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); }
}二、Spring 工厂+策略模式的适用场景
使用 “工厂+策略模式” 比较适合通过某些参数,去执行不同的业务逻辑的这种场景,能很好的解决执行逻辑相同,调用上游接口逻辑不同这种情形。

三、Spring 工厂+策略模式的示例项目
Maven 引入 Springfox 依赖
新建 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;
@Componentpublic 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); }
}创建测试的 Controller 类
创建用于测试的接口,里面实现了接收不同的产品类型,去工厂里获取不同的产品下单实现类,然后调用不同的产品下单实现类去执行下单订购的业务逻辑。
import mydlq.club.example.service.ProductService;import mydlq.club.example.service.factory.ProductStrategyFactory;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
@RestControllerpublic 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 启动类
创建启动类,用于启动 SpringBoot 应用。
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic 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 比使用 “工厂+策略模式” 更加简单,更加方便开发人员实现业务代码逻辑。
