oj代码runtime error_策略模式+注解 干掉业务代码中冗余的if else...

a68551411e99d9523e3b775388e2a5ac.gif

前言:

之前写过一个工作中常见升级模式-策略模式 的文章,里面讲了具体是怎样使用策略模式去抽象现实中的业务代码,今天来拿出实际代码来写个demo,这里做个整理来加深自己对策略模式的理解。

一、业务流程

取消订单会有多种情况,比如:取消即时订单、取消预约订单、取消拼车订单

一般业务代码中我们会根据取消类型来进行不同的逻辑处理,代码中无外乎多了很多if else的业务逻辑,且代码耦合度很高。

那么有没有一种优雅的处理方式呢? 当然有了,现在就来说下我们系统中是如何处理这种问题的( 具体业务需求可以参考我上一篇文章:https://www.cnblogs.com/wang-meng/p/11457544.html)

二、流程图

上面已经说了业务流程,当然有流程图才是最直观的,这里我们可以直接参见下图:

54db31bd1966242e23777b929c97b6d3.png

这里我们可以使用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

2fe8e3a081192be2e3d9e51df687af15.png

 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 }

2fe8e3a081192be2e3d9e51df687af15.png

这里会显示该取消订单的类型,如过往代码中所有if 中的条件判断,这里用枚举进行归纳。

3、构建抽象策略及策略实现类

策略抽象类:AbstractOrderStrategy

2fe8e3a081192be2e3d9e51df687af15.png

1 public abstract class AbstractOrderStrategy {2  3     /**4      * 策略抽象方法5      * @param orderDTO6      */7     abstract public void process(OrderDTO orderDTO);8 }

2fe8e3a081192be2e3d9e51df687af15.png

为了容易理解,这里只有一个业务实现类,继承该抽象类的策略类都会实现具体的业务。

预约单处理策略类:BookingOrderStrategy

2fe8e3a081192be2e3d9e51df687af15.png

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 }

2fe8e3a081192be2e3d9e51df687af15.png

这里重点关注orderType,代码区分具体的执行策略 都是通过这个type去进行处理的。

即时单处理策略类:InstantOrderStrategy

2fe8e3a081192be2e3d9e51df687af15.png

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 }

2fe8e3a081192be2e3d9e51df687af15.png

4、策略分发处理类

这个类主要是接收业务请求,然后转发到具体的策略类进行处理,这里使用到了spring 获取具体的类,然后通过类上面的注解信息进行转发。

2fe8e3a081192be2e3d9e51df687af15.png

 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 }

2fe8e3a081192be2e3d9e51df687af15.png

其中:

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工具类

2fe8e3a081192be2e3d9e51df687af15.png

 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 }

2fe8e3a081192be2e3d9e51df687af15.png

6、添加单元测试

2fe8e3a081192be2e3d9e51df687af15.png

public class CancelAbstractOrderStrategyTest extends BaseTest {
@Autowiredprivate CancelOrderStrategyService cancelOrderStrategyService;
@Testpublic void process() {
OrderDTO orderDTO = new OrderDTO();
orderDTO.setServiceType(OrderTypeEnum.INSTANT.getCode());
cancelOrderStrategyService.process(orderDTO);
}
}

2fe8e3a081192be2e3d9e51df687af15.png

7、打印结果:

1 取消即时订单

到了这里代码就已经写完了,如果仔细看的话 应该会明白这里的设计思路,通过策略模式+注解 可以大大的降低业务的耦合度,而且也极大的方便了后期维护的工作量。

1d6eb9f96798bc2e8addb3d5b0ec0869.png

0e9036201befd17ee69709c2bf28465a.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值