策略模式+註解 幹掉業務代碼中冗餘的if else...

前言:

以前寫過一個工做中常見升級模式-策略模式 的文章,裏面講了具體是怎樣使用策略模式去抽象現實中的業務代碼,今天來拿出實際代碼來寫個demo,這裏作個整理來加深本身對策略模式的理解。
 

1、業務流程

取消訂單會有多種狀況,好比:取消即時訂單、取消預定訂單、取消拼車訂單
 
通常業務代碼中咱們會根據取消類型來進行不一樣的邏輯處理,代碼中無外乎多了不少if else的業務邏輯,且代碼耦合度很高。
 
那麼有沒有一種優雅的處理方式呢? 固然有了,如今就來講下咱們系統中是如何處理這種問題的( 具體業務需求能夠參考我上一篇文章: http://www.javashuo.com/article/p-abyiayop-hu.html
 

2、流程圖

上面已經說了業務流程,固然有流程圖纔是最直觀的,這裏咱們能夠直接參見下圖:
 
 
這裏咱們可使用Spring 獲取到全部 AbstractOrderStrategy 實現類中@OrderTypeAnnotation註解的類,而後比較orderType便可。下面就看具體代碼吧;
 

3、代碼實現及解析

一、自定義訂單類型枚舉:OrderTypeAnnotation
 
1 @Target({ElementType.TYPE})
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface OrderTypeAnnotation {
4     OrderTypeEnum orderType();
5 }
這裏 ElementType.TYPE表示用於類上,RetentionPolicy.RUNTIME 表示運行時解析的。更多的含義你們能夠看註解的相關知識。
 
二、構建OrderType枚舉:OrderTypeEnum
 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 中的條件判斷,這裏用枚舉進行概括。
 
三、構建抽象策略及策略實現類
 
策略抽象類: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 }
 
四、策略分發處理類
 
這個類主要是接收業務請求,而後轉發到具體的策略類進行處理,這裏使用到了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信息來獲取具體的代理類。
 
 
五、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 }

 

 
六、添加單元測試
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);
    }
}
 
七、打印結果:
1 取消即時訂單
 
到了這裏代碼就已經寫完了,若是仔細看的話 應該會明白這裏的設計思路,經過策略模式+註解 能夠大大的下降業務的耦合度,並且也極大的方便了後期維護的工做量。
 
部分代碼參考: http://www.javashuo.com/article/p-fcoesusa-gh.html, 這裏對核心類作了修改和精簡。
相關文章
相關標籤/搜索