工厂模式和策略模式:
在Java的设计模式中,工厂模式和策略模式都是很常见且实用的模式。它们各自解决了不同的问题,在复杂的系统设计中,这两种模式往往会结合使用,以提升代码的灵活性、可维护性和扩展性。
1、工厂模式(Factory Pattern)是一种创建型设计模式,旨在定义一个接口来创建对象,但让子类决定实例化哪一个类。工厂模式让一个类的实例化延迟到其子类。
2、策略模式(Strategy Pattern)是一种行为型设计模式,定义了一系列算法,并将每个算法封装起来,使它们可以互换使用。策略模式使得算法可以在不影响客户端的情况下发生变化。
1. 需求背景
假设我们需要开发一个订单处理系统,系统需要根据不同的支付方式(如信用卡、PayPal、比特币等)进行不同的处理。不同的支付方式有不同的处理策略,并且系统需要支持将来扩展新的支付方式。
2. 设计思路
使用策略模式:将不同的支付方式封装为不同的策略,实现支付的多样化处理。
使用工厂模式:工厂模式负责根据用户选择的支付方式创建和选择相应的支付策略对象。执行业务逻辑。
业务场景:
1、营销系统,根据不同业务渠道推送消息,比如:微信消息推送、钉钉工作通知、短信、邮件通知
2、系统支付,业务选择不同支付方式,执行不同的业务逻辑和处理措施。比如:微信、支付宝、银联等
3、系统登录,根据需求选择不同登录方式,触发后台不同的业务执行逻辑。比如:微信、钉钉、短信、支付宝等不同的登录渠道进行授权,完成业务系统登录
解决方案(如下图):
1、在业务层使用 if-else 判断类型,去执行不同的业务逻辑。
2、使用工厂模式+策略设计模式,同时结合SpringBoot注解选择不同的策略,实现不同的业务逻辑。
代码示例
定义枚举
@AllArgsConstructor
@Getter
public enum SaleOrderEnum {
SHOP_SALE_ORDER("10001", "店铺业务零售单"),
SUPPLY_SALE_ORDER("10002","供应链直销单"),
;
private static final Map<Long, SaleOrderEnum > ORDER_MAP = new HashMap<>(32);
static {
for (SaleOrderEnum orderStatusEnum : SaleOrderEnum .values()) {
ORDER_STATUS_MAP.put(orderStatusEnum.getCode(), orderStatusEnum);
}
}
public static SaleOrderEnum findByCode(String code) {
// 这里可以直接调用getSaleOrdeEnum()方法也可以获取。
for (SaleOrderEnum orderEnum : SaleOrderEnum.values()) {
if (orderEnum.getCode().equals(code)) {
return orderEnum;
}
}
throw new IllegalArgumentException("Please check the passed parameters");
}
private String code;
private String name;
public static SaleOrderEnum getSaleOrdeEnum(Long code) {
return ORDER_STATUS_MAP.get(code);
}
}
定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OrderStrategyType {
SaleOrderEnum orderType();
}
描述:定义这个注解的目的,是在不同的策略上使用的,在工厂中就可以根据注解查找到所有的策略,并保存进 map 中映射起来,一旦有参数进来,就去 map 中去查找对应的策略,就可以执行对应的方法了,减少if-else代码块优化代码,同时可以保证代码的扩展性。
定义策略类的接口
首先定义一个策略接口,然后编写业务类去实现
public interface OrderStrategy {
/**
* 查询数据方法
*
* @return 返回结果
*/
List<Object> executeOrderBiz();
}
1、门店业务实现类、实现工单策略处理接口,编写业务执行逻辑
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SHOP_SALE_ORDER)
public class ShopSaleOrderStrategy implements OrderStrategy {
@Override
public List<Object> executeOrderBiz() {
log.info("执行门店销售单业务功能......");
return Arrays.asList("业务工单1", "业务工单2", "业务工单3");
}
}
2、供应链业务实现类、实现工单策略处理接口,编写业务执行逻辑
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SUPPLY_SALE_ORDER)
public class SupplySaleOrderStrategy implements OrderStrategy {
@Override
public List<Object> executeOrderBiz() {
log.info("执行供应链销售单业务功能......");
return Arrays.asList("供应链工单1","供应链工单2");
}
}
注意:我们要把具体的业务实现类策略交给 Spring 管理,所以加上了 @Component 注解。
定义工厂类(业务调用通用入口)
@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));
}
public OrderStrategyFactory(List<OrderStrategy> strategyList) {
this.strategyList = strategyList;
}
@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,当然也可以进行依赖注入,这就是Spring中IOC容器特性。
所以在工厂类中,我们定义了一个 strategyList 属性,并且使用 @RequiredArgsConstructor 注解,就是使用构造器的方式将属性注入。所以当 spring 容器启动的时候,就会查找所有策略,并注入到工厂类的属性当中。这是 spring 的依赖注入的原理。
我们使用 @PostContruct 注解,当工厂类加载完毕之后,就会执行 init()方法。这时候,因为 orderStrategyFactory 这个 bean 已经在 spring 容器中加载完毕了,并且也注入了属性,所以这个 strategyList 就有值了,我们可以遍历这个列表,获取到对应 OrderStrategyType 这个注解的信息,然后放入到 map 当中,后面我们就可以根据枚举类型将不同的策略对应起来了。
使用层面:业务调用示例(使用案例)
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping(("/getList")
public List<Object> getList(@RequestParam String type){
OrderStrategy strategy = OrderStrategyFactory.getStrategy(type);
return strategy.executeOrderBiz();
}
}
总结:
这样就可以避免代码中的if-else,同时可以动态增加业务实现类。
比如增加其他业务实现类,直接实现OrderStrategy 接口即可,同时修改每个执行业务的逻辑,不会相互影响。使用时,我们就可以在枚举类中加入对应的类型,在具体的策略实现类中加入注解,并指定对应的枚举类型,这样我们就可以完成我们的业务需求了。
补充描述:上面实现接口、也可以继承抽象类
定义业务抽象类
public abstract class OrderStrategy {
public abstract List<Object> executeOrderBiz();
}
使用案例:将实现类中原来的implements 变成extends 其他代码完全一致
@Slf4j
@Component
@RequiredArgsConstructor
@OrderStrategyType(orderType = SaleOrderEnum.SHOP_SALE_ORDER)
public class ShopSaleOrderStrategy extends OrderStrategy {
@Override
public List<Object> executeOrderBiz() {
log.info("执行门店销售单业务功能......");
return Arrays.asList("业务工单1", "业务工单2", "业务工单3");
}
}