前言:
之前写过一个工作中常见升级模式-策略模式 的文章,里面讲了具体是怎样使用策略模式去抽象现实中的业务代码,今天来拿出实际代码来写个demo,这里做个整理来加深自己对策略模式的理解。
  
一、业务流程
取消订单会有多种情况,比如:取消即时订单、取消预约订单、取消拼车订单   
一般业务代码中我们会根据取消类型来进行不同的逻辑处理,代码中无外乎多了很多if else的业务逻辑,且代码耦合度很高。   
那么有没有一种优雅的处理方式呢? 当然有了,现在就来说下我们系统中是如何处理这种问题的( 具体业务需求可以参考我上一篇文章:
https://www.cnblogs.com/wang-meng/p/11457544.html 
<https://www.cnblogs.com/wang-meng/p/11457544.html>)   
二、流程图
上面已经说了业务流程,当然有流程图才是最直观的,这里我们可以直接参见下图:     
这里我们可以使用Spring 获取到所有 AbstractOrderStrategy 实现类中@OrderTypeAnnotation注解的类,然后比较orderType即可。下面就看具体代码吧;
  
三、代码实现及解析
1、自定义订单类型枚举:OrderTypeAnnotation   1 @Target({ElementType.TYPE}) 2 
@Retention(RetentionPolicy.RUNTIME)3 public @interface OrderTypeAnnotation { 4  
OrderTypeEnum orderType();5 } 这里 
ElementType.TYPE表示用于类上,RetentionPolicy.RUNTIME 表示运行时解析的。更多的含义大家可以看注解的相关知识。   
2、构建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 中的条件判断,这里用枚举进行归纳。   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 { 
@Autowiredprivate 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 
<https://www.cnblogs.com/HelloDeveloper/p/11390512.html>, 这里对核心类做了修改和精简。 
热门工具 换一换