一、Spring 接口中使用 If Else 的不足
在我们日常使用 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);
}
}
二、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;
@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);
}
}
创建测试的 Controller 类
创建用于测试的接口,里面实现了接收不同的产品类型,去工厂里获取不同的产品下单实现类,然后调用不同的产品下单实现类去执行下单订购的业务逻辑。
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 启动类
创建启动类,用于启动 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---