小编在公司负责的就是订单取消业务,老系统中各种类型订单取消都是通过if else 判断不同的订单类型进行不同的逻辑。在经历老系统的折磨和产品需求的不断变更,小编决定进行一次大的重构:消灭 if else。
接下来就向大家介绍下是如何消灭 if else。
1. if else模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Service public class CancelOrderService {
public void process(OrderDTO orderDTO) { int serviceType = orderDTO.getServiceType(); if (1 == serviceType) { System.out.println("取消即时订单"); } else if (2 == serviceType) { System.out.println("取消预约订单"); } else if (3 == serviceType) { System.out.println("取消拼车订单"); } } }
|
若干个月再来看就是这样的感觉
2. 策略模式
2.1 策略模式实现的Service
1 2 3 4 5 6 7 8 9 10 11 12
| @Service public class CancelOrderStrategyService {
@Autowired private StrategyContext context;
public void process(OrderDTO orderDTO) { OrderTypeEnum orderTypeEnum = OrderTypeEnum.getByCode(orderDTO.getServiceType()); AbstractStrategy strategy = context.getStrategy(orderTypeEnum); strategy.process(orderDTO); } }
|
简洁的有点过分了是不是!!!
2.2 各种类型策略实现及抽象策略类
下面选取了即时订单和预约订单的策略.
1 2 3 4 5 6 7 8
| @Service @OrderTypeAnnotation(orderType = OrderTypeEnum.INSTANT) public class InstantOrderStrategy extends AbstractStrategy { @Override public void process(OrderDTO orderDTO) { System.out.println("取消即时订单"); } }
|
1 2 3 4 5 6 7 8
| @Service @OrderTypeAnnotation(orderType = OrderTypeEnum.BOOKING) public class BookingOrderStrategy extends AbstractStrategy { @Override public void process(OrderDTO orderDTO) { System.out.println("取消预约订单"); } }
|
1 2 3
| public abstract class AbstractStrategy { abstract public void process(OrderDTO orderDTO); }
|
2.3 策略类型注解
每个策略中增加了注解OrderTypeAnnotation,以标注适用于不同类型的策略内容.
1 2 3 4 5 6 7
| @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface OrderTypeAnnotation { OrderTypeEnum orderType(); }
|
2.4 策略处理器类StrategyProcessor和策略上下文StrategyContext
其中最为核心的为StrategyProcessor 策略处理器类和StrategyContext 策略上下文,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Component public class StrategyProcessor implements BeanFactoryPostProcessor {
private static final String STRATEGY_PACKAGE = "com.lujiahao.strategy";
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { Map<OrderTypeEnum, Class> handlerMap = Maps.newHashMapWithExpectedSize(3); ClassScanner.scan(STRATEGY_PACKAGE, OrderTypeAnnotation.class).forEach(clazz -> { OrderTypeEnum type = clazz.getAnnotation(OrderTypeAnnotation.class).orderType(); handlerMap.put(type, clazz); });
StrategyContext context = new StrategyContext(handlerMap); configurableListableBeanFactory.registerSingleton(StrategyContext.class.getName(), context); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class StrategyContext { private Map<OrderTypeEnum, Class> strategyMap;
public StrategyContext(Map<OrderTypeEnum, Class> strategyMap) { this.strategyMap = strategyMap; }
public AbstractStrategy getStrategy(OrderTypeEnum orderTypeEnum) { if (orderTypeEnum == null) { throw new IllegalArgumentException("not fond enum"); }
if (CollectionUtils.isEmpty(strategyMap)) { throw new IllegalArgumentException("strategy map is empty,please check you strategy package path"); }
Class clazz = strategyMap.get(orderTypeEnum); if (clazz == null) { throw new IllegalArgumentException("not fond strategy for type:" + orderTypeEnum.getCode()); }
return (AbstractStrategy) SpringBeanUtils.getBean(clazz); } }
|
- 首先会扫描指定包中标有@OrderTypeAnnotation的类
- 将符合类的对应的枚举值作为key,对应的类作为value,保存在策略Map中
- 初始化StrategyContext,并注册到spring容器中,同时将策略Map传入其中
我们使用了枚举作为Map中的key,相信大家很少有人这样操作过,不过可以放心操作.通过下面两篇文章解答大家的疑问.
3. 总结
策略模式极大的减少if else等模板代码,在提升代码可读性的同时,也大大增加代码的灵活性,添加新的策略即可以满足业务需求.
本人在我司业务中对策略模式的应用得到了很好的验证,从此再也不用担心产品改需求.
用策略模式一时爽,一直用一直爽😏!
4. 代码
完整代码
欢迎大家关注😁