前言:
之前写过一个工作中常见升级模式-策略模式 的文章,里面讲了具体是怎样使用策略模式去抽象现实中的业务代码,今天来拿出实际代码来写个demo,这里做个整理来加深自己对策略模式的理解。
一、业务流程
取消订单会有多种情况,比如:取消即时订单、取消预约订单、取消拼车订单
一般业务代码中我们会根据取消类型来进行不同的逻辑处理,代码中无外乎多了很多if else的业务逻辑,且代码耦合度很高。
那么有没有一种优雅的处理方式呢? 当然有了,现在就来说下我们系统中是如何处理这种问题的( 具体业务需求可以参考我上一篇文章: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 orderDTO6 */7 abstract public void process(OrderDTO orderDTO);8 }
为了容易理解,这里只有一个业务实现类,继承该抽象类的策略类都会实现具体的业务。
预约单处理策略类:BookingOrderStrategy
1 @Service2 @OrderTypeAnnotation(orderType = OrderTypeEnum.BOOKING)3 public class BookingOrderStrategy extends AbstractOrderStrategy {4 5 @Override6 public void process(OrderDTO orderDTO) {7 System.out.println("取消预约订单");8 }9 }
这里重点关注orderType,代码区分具体的执行策略 都是通过这个type去进行处理的。
即时单处理策略类:InstantOrderStrategy
1 @Service2 @OrderTypeAnnotation(orderType = OrderTypeEnum.INSTANT)3 public class InstantOrderStrategy extends AbstractOrderStrategy {4 5 @Override6 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 beanMap = SpringBeanUtils.getBeanMap(AbstractOrderStrategy.class); 9 try {10 for (Map.Entry 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 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 getBean(Class clazz) {17 return applicationContext.getBean(clazz);18 }19 20 /**21 * 获取类型为requiredType的Map22 *23 * @param clazz24 * @return25 */26 public static Map getBeanMap(Class clazz) {27 return applicationContext.getBeansOfType(clazz);28 }29 30 /**31 * 获取 目标对象32 *33 * @param proxy 代理对象34 * @return 目标对象35 * @throws Exception36 */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 // cglib47 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;
@Testpublic void process() {
OrderDTO orderDTO = new OrderDTO();
orderDTO.setServiceType(OrderTypeEnum.INSTANT.getCode());
cancelOrderStrategyService.process(orderDTO);
}
}
7、打印结果:
1 取消即时订单
到了这里代码就已经写完了,如果仔细看的话 应该会明白这里的设计思路,通过策略模式+注解 可以大大的降低业务的耦合度,而且也极大的方便了后期维护的工作量。