還在用if else?策略模式瞭解一下!
阿新 • • 發佈:2019-08-21
在公司負責的就是訂單取消業務,老系統中各種型別訂單取消都是通過if else 判斷不同的訂單型別進行不同的邏輯。在經歷老系統的折磨和產品需求的不斷變更,決定進行一次大的重構:消滅 if else。
接下來就向大家介紹下是如何消滅 if else。
1. if else模式
@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
@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 各種型別策略實現及抽象策略類
下面選取了即時訂單和預約訂單的策略.
@Service
@OrderTypeAnnotation(orderType = OrderTypeEnum.INSTANT)
public class InstantOrderStrategy extends AbstractStrategy {
@Override
public void process(OrderDTO orderDTO) {
System.out.println("取消即時訂單");
}
}
@Service @OrderTypeAnnotation(orderType = OrderTypeEnum.BOOKING) public class BookingOrderStrategy extends AbstractStrategy { @Override public void process(OrderDTO orderDTO) { System.out.println("取消預約訂單"); } }
public abstract class AbstractStrategy {
abstract public void process(OrderDTO orderDTO);
}
2.3 策略型別註解
每個策略中增加了註解OrderTypeAnnotation,以標註適用於不同型別的策略內容.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OrderTypeAnnotation {
OrderTypeEnum orderType();
}
2.4 策略處理器類StrategyProcessor和策略上下文StrategyContext
其中最為核心的為StrategyProcessor 策略處理器類和StrategyContext 策略上下文,
@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);
}
}
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,相信大家很少有人這樣操作過,不過可以放心操作.通過下面兩篇文章解答大家的疑問.
- 自定義列舉類 Enum 是否可以作為 HashMap 的key
- Java 在 Map 中使用複雜資料型別作為 Key
3. 總結
策略模式極大的減少if else等模板程式碼,在提升程式碼可讀性的同時,也大大增加程式碼的靈活性,新增新的策略即可以滿足業務需求.
本人在我司業務中對策略模式的應用得到了很好的驗證,從此再也不用擔心產品改需求.
用策略模式一時爽,一直用一直爽