策略模式+註解 幹掉業務程式碼中冗餘的if else...
阿新 • • 發佈:2019-09-18
前言:
之前寫過一個工作中常見升級模式-策略模式 的文章,裡面講了具體是怎樣使用策略模式去抽象現實中的業務程式碼,今天來拿出實際程式碼來寫個demo,這裡做個整理來加深自己對策略模式的理解。一、業務流程
取消訂單會有多種情況,比如:取消即時訂單、取消預約訂單、取消拼車訂單 一般業務程式碼中我們會根據取消型別來進行不同的邏輯處理,程式碼中無外乎多了很多if else的業務邏輯,且程式碼耦合度很高。 那麼有沒有一種優雅的處理方式呢? 當然有了,現在就來說下我們系統中是如何處理這種問題的( 具體業務需求可以參考我上一篇文章:https://www.cnblogs.com/wang-meng/p/11457544.html)二、流程圖
三、程式碼實現及解析
1、自定義訂單型別列舉:OrderTypeAnnotation1 @Target({ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface OrderTypeAnnotation { 4 OrderTypeEnum orderType(); 5 }
1 public enum OrderTypeEnum { 2 INSTANT(1, "即時訂單"), 3 BOOKING(2, "預約訂單"), 4 CARPOOL(3, "拼車訂單"); 5 6 7 private int code; 8 private String desc; 9 10 11 OrderTypeEnum(int code, String desc) { 12 this.code = code; 13 this.desc = desc; 14 } 15 16 17 public int getCode() { 18 return code; 19 } 20 21 22 public String getDesc() { 23 return desc; 24 } 25 26 27 public static OrderTypeEnum getByCode(int code) { 28 for (OrderTypeEnum orderTypeEnum : values()) { 29 if (orderTypeEnum.getCode() == code) { 30 return orderTypeEnum; 31 } 32 } 33 return null; 34 } 35 }
這裡會顯示該取消訂單的型別,如過往程式碼中所有if 中的條件判斷,這裡用列舉進行歸納。 3、構建抽象策略及策略實現類 策略抽象類:AbstractOrderStrategy
1 public abstract class AbstractOrderStrategy { 2 3 /** 4 * 策略抽象方法 5 * @param orderDTO 6 */ 7 abstract public void process(OrderDTO orderDTO); 8 }為了容易理解,這裡只有一個業務實現類,繼承該抽象類的策略類都會實現具體的業務。 預約單處理策略類:BookingOrderStrategy
1 @Service 2 @OrderTypeAnnotation(orderType = OrderTypeEnum.BOOKING) 3 public class BookingOrderStrategy extends AbstractOrderStrategy { 4 5 @Override 6 public void process(OrderDTO orderDTO) { 7 System.out.println("取消預約訂單"); 8 } 9 }這裡重點關注orderType,程式碼區分具體的執行策略 都是通過這個type去進行處理的。 即時單處理策略類:InstantOrderStrategy
1 @Service 2 @OrderTypeAnnotation(orderType = OrderTypeEnum.INSTANT) 3 public class InstantOrderStrategy extends AbstractOrderStrategy { 4 5 @Override 6 public void process(OrderDTO orderDTO) { 7 System.out.println("取消即時訂單"); 8 } 9 }4、策略分發處理類 這個類主要是接收業務請求,然後轉發到具體的策略類進行處理,這裡使用到了spring 獲取具體的類,然後通過類上面的註解資訊進行轉發。
1 @Service 2 @Slf4j 3 public class CancelOrderStrategyService { 4 /** 5 * 處理取消邏輯 6 */ 7 public void process(OrderDTO orderDTO) { 8 Map<String, AbstractOrderStrategy> beanMap = SpringBeanUtils.getBeanMap(AbstractOrderStrategy.class); 9 try { 10 for (Map.Entry<String, AbstractOrderStrategy> entry : beanMap.entrySet()) { 11 Object real = SpringBeanUtils.getTarget(entry.getValue()); 12 OrderTypeAnnotation annotation = real.getClass().getAnnotation(OrderTypeAnnotation.class); 13 if (orderDTO.getServiceType() == annotation.orderType().getCode()) { 14 entry.getValue().process(orderDTO); 15 break; 16 } 17 } 18 } catch (Exception e) { 19 log.error("獲取目標代理物件失敗:{}", e); 20 } 21 } 22 }
其中: Map<String, AbstractOrderStrategy> beanMap = SpringBeanUtils.getBeanMap(AbstractOrderStrategy.class); 代表獲取AbstractOrderStrategy 下全部子類或介面。 Object real = SpringBeanUtils.getTarget(entry.getValue()); 代表獲取具體的代理類 OrderTypeAnnotation annotation = real.getClass().getAnnotation(OrderTypeAnnotation.class); 代表取類上有OrderTypeAnnotation 的註解資訊。 CancelOrderStrategyService.process() 這個方法是可以擴充套件的,通過外部傳入的class資訊來獲取具體的代理類。 5、Spring獲取bean工具類
1 @Component 2 public class SpringBeanUtils implements ApplicationContextAware { 3 private static ApplicationContext applicationContext; 4 5 @Override 6 public void setApplicationContext(ApplicationContext context) throws BeansException { 7 if (applicationContext == null) { 8 applicationContext = context; 9 } 10 } 11 12 public static Object getBean(String name) { 13 return applicationContext.getBean(name); 14 } 15 16 public static <T> T getBean(Class<T> clazz) { 17 return applicationContext.getBean(clazz); 18 } 19 20 /** 21 * 獲取型別為requiredType的Map 22 * 23 * @param clazz 24 * @return 25 */ 26 public static <T> Map<String, T> getBeanMap(Class<T> clazz) { 27 return applicationContext.getBeansOfType(clazz); 28 } 29 30 /** 31 * 獲取 目標物件 32 * 33 * @param proxy 代理物件 34 * @return 目標物件 35 * @throws Exception 36 */ 37 public static Object getTarget(Object proxy) throws Exception { 38 if (!AopUtils.isAopProxy(proxy)) { 39 // 不是代理物件,直接返回 40 return proxy; 41 } 42 43 if (AopUtils.isJdkDynamicProxy(proxy)) { 44 return getJdkDynamicProxyTargetObject(proxy); 45 } else { 46 // cglib 47 return getCglibProxyTargetObject(proxy); 48 } 49 } 50 51 private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { 52 Field field = proxy.getClass().getSuperclass().getDeclaredField("h"); 53 field.setAccessible(true); 54 AopProxy aopProxy = (AopProxy) field.get(proxy); 55 Field advised = aopProxy.getClass().getDeclaredField("advised"); 56 advised.setAccessible(true); 57 58 Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget(); 59 return target; 60 } 61 62 private static Object getCglibProxyTargetObject(Object proxy) throws Exception { 63 Field field = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); 64 field.setAccessible(true); 65 Object dynamicAdvisedInterceptor = field.get(proxy); 66 67 Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); 68 advised.setAccessible(true); 69 70 Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); 71 return target; 72 } 73 }
6、新增單元測試
public class CancelAbstractOrderStrategyTest extends BaseTest { @Autowired private CancelOrderStrategyService cancelOrderStrategyService; @Test public void process() { OrderDTO orderDTO = new OrderDTO(); orderDTO.setServiceType(OrderTypeEnum.INSTANT.getCode()); cancelOrderStrategyService.process(orderDTO); } }7、列印結果:
1 取消即時訂單到了這裡程式碼就已經寫完了,如果仔細看的話 應該會明白這裡的設計思路,通過策略模式+註解 可以大大的降低業務的耦合度,而且也極大的方便了後期維護的工作量。 部分程式碼參考:https://www.cnblogs.com/HelloDeveloper/p/11390512.html, 這裡對核心類做了修改和精簡