工厂+策略在springboot项目中的使用场景

1,881 阅读3分钟

业务场景

我们需要做一个报表,前端传进不同的类型,需要去数据库中不同的表格去查找,将数据展示出来。
也就是说,虽然是不同数据库的表,但是因为数据结构是一样的,所以我们只需要提供一个接口给前端,返回相同的数据结构。后端需要做的事情,就是根据不同的类型去不同的数据库表中查找数据,拼接成一个视图对象返回给前端。

解决方案

  1. 在业务层使用if-else判断类型,去不同的数据表中查找。
  2. 使用工厂模式结合策略模式,再使用注解来选择不同的策略,实现不同的业务逻辑。

我们选用的是第二种方式。

1684820399867.png

代码示例

定义枚举

@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();
    }
}

总结

这样,一旦我们有加入其它表的查询,但是返回给前端的数据结构是一样的,我们就可以在枚举类中加入对应的类型,在具体的策略实现类中加入注解,并指定对应的枚举类型,这样我们就可以完成我们的业务需求了。