還在用if else?策略模式了解一下!

  • 2019 年 10 月 3 日
  • 筆記

在公司負責的就是訂單取消業務,老系統中各種類型訂單取消都是通過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,相信大家很少有人這樣操作過,不過可以放心操作.通過下面兩篇文章解答大家的疑問.

3. 總結

策略模式極大的減少if else等模板程式碼,在提升程式碼可讀性的同時,也大大增加程式碼的靈活性,添加新的策略即可以滿足業務需求.
本人在我司業務中對策略模式的應用得到了很好的驗證,從此再也不用擔心產品改需求.
用策略模式一時爽,一直用一直爽?!

4. 程式碼

完整程式碼

Tips

  • 歡迎收藏和轉發,感謝你的支援!(๑•̀ㅂ•́)و✧
  • 歡迎關注我的公眾號:莊裡程式猿,讀書筆記教程資源第一時間獲得!