业务场景
我们需要做一个报表,前端传进不同的类型,需要去数据库中不同的表格去查找,将数据展示出来。也就是说,虽然是不同数据库的表,但是因为数据结构是一样的,所以我们只需要提供一个接口给前端,返回相同的数据结构。后端需要做的事情,就是根据不同的类型去不同的数据库表中查找数据,拼接成一个视图对象返回给前端。
解决方案
在业务层使用 if-else 判断类型,去不同的数据表中查找。
使用工厂模式结合策略模式,再使用注解来选择不同的策略,实现不同的业务逻辑。
我们选用的是第二种方式。
代码示例
定义枚举
@AllArgsConstructor
@Getter
public enum SaleOrderEnum {
SHOP_SALE_ORDER("10001", "门店销售单"),
SUPPLY_SALE_ORDER("10002","供应链销售单");
public static SaleOrderEnum findByCode(String code) {
for (SaleOrderEnum orderEnum : SaleOrderEnum.values()) {
if (orderEnum.getCode().equals(code)) {
return orderEnum;
}
}
throw new IllegalArgumentException("code is invalid");
}
private String code;
private String name;
}
复制代码
定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OrderStrategyType {
SaleOrderEnum orderType();
}
复制代码
我定义这个注解的目的,是在不同的策略上使用的,在工厂中就可以根据注解查找到所有的策略,并保存进 map 中一一对应起来,一旦有参数进来,就去 map 中去查找对应的策略,就可以执行对应的方法了。
定义抽象的策略类
public abstract class OrderStrategy {
public abstract List<Object> queryList();
}
复制代码
定义具体的策略实现
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SHOP_SALE_ORDER)
public class ShopSaleOrderStrategy extends OrderStrategy {
@Override
public List<Object> queryList() {
log.info("查询门店销售单列表......");
return Arrays.asList("门店销售单1","门店销售单2","门店销售单3");
}
}
复制代码
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SUPPLY_SALE_ORDER)
public class SupplySaleOrderStrategy extends OrderStrategy {
@Override
public List<Object> queryList() {
log.info("查询供应链销售单列表......");
return Arrays.asList("供应链销售单1","供应链销售单2");
}
}
复制代码
注意,我们是要把我们具体的策略交给 spring 管理,所以我们加上了 @Service 注解。
定义工厂类
@Component
@RequiredArgsConstructor
public class OrderStrategyFactory {
/**
* spring会帮我们注入所有OrderStrategy的子类
*/
private final List<OrderStrategy> strategyList;
private static Map<SaleOrderEnum, OrderStrategy> map = new ConcurrentHashMap<>();
public static OrderStrategy getStrategy(String code){
return map.get(SaleOrderEnum.findByCode(code));
}
@PostConstruct
public void init(){
strategyList.forEach(i -> {
OrderStrategyType annotation = AnnotationUtils.findAnnotation(i.getClass(), OrderStrategyType.class);
map.put(annotation.orderType(),i);
});
}
}
复制代码
因为上面我们已经把具体的策略,像 supplySaleOrderStrategy、shopSaleOrderStrategy 这些策略的实现,交给 spring 管理了,也就是说我们可以从 spring 的容器中获取到对应的 bean,当然也可以进行依赖注入。
所以在工厂类中,我们定义了一个 strategyList 属性,并且使用 @RequiredArgsConstructor 注解,就是使用构造器的方式将属性注入。所以当 spring 容器启动的时候,就会查找所有策略,并注入到工厂类的属性当中。这是 spring 的依赖注入的原理。
我们使用 @PostContruct 注解,当工厂类加载完毕之后,就会执行 init()方法。这时候,因为 orderStrategyFactory 这个 bean 已经在 spring 容器中加载完毕了,并且也注入了属性,所以这个 strategyList 就有值了,我们可以遍历这个列表,获取到对应 OrderStrategyType 这个注解的信息,然后放入到 map 当中,后面我们就可以根据枚举类型将不同的策略对应起来了。
定义 controller
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/getList")
public List<Object> getList(@RequestParam String type){
OrderStrategy strategy = OrderStrategyFactory.getStrategy(type);
return strategy.queryList();
}
}
复制代码
总结
这样,一旦我们有加入其它表的查询,但是返回给前端的数据结构是一样的,我们就可以在枚举类中加入对应的类型,在具体的策略实现类中加入注解,并指定对应的枚举类型,这样我们就可以完成我们的业务需求了。
作者:人生密密缝
链接:https://juejin.cn/post/7236217091967008826
来源:稀土掘金
评论