注解实现策略模式干掉if-else(下)
上篇回顾
上一篇(注解实现策略模式干掉if-else(上))中我们介绍了如何通过注解实现策略模式,定义@OrderHandlerType注解和对应的注解实现类OrderHandlerTypeImpl,并维护了类型为Map<OrderHandlerType, OrderHandler>的orderHandleMap,完成了订单来源与支付方式到订单处理器Handler的映射关系,使得我们在选择订单处理器Handler时更加灵活,更易扩展。
但是在上篇中遗留了一个问题,那就是如果PC端支付宝支付和微信支付是同一种处理逻辑,而移动端支付宝支付和微信支付是不同的处理逻辑,那情况就变成了PCAliPayOrderHandler和PCWeChatOrderHandler这两个类是同一套代码逻辑,本篇讲的就是如何应对这种具有相同处理逻辑的情况。
代码重复的情况
现在我们有以下四个OrderHandler的实现类,分别处理PC端支付宝支付的订单、PC端微信支付的订单、移动端支付宝支付的订单和移动端微信支付的订单:
@OrderHandlerType(source = "pc", payMethod = "alipay") public class PCAliPayOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理PC端通过支付宝支付的订单"); } } ==========分隔线========== @OrderHandlerType(source = "pc",payMethod = "weChat") public class PCWeChatOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理PC端通过微信支付的订单"); } } ==========分隔线========== @OrderHandlerType(source = "mobile",payMethod = "alipay") public class MobileAliPayOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理移动端通过支付宝支付的订单"); } } ==========分隔线========== @OrderHandlerType(source = "mobile", payMethod = "weChat") public class MobileWeChatOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理移动端通过微信支付的订单"); } }
现在,我们业务要求了PC端支付宝支付的订单和PC端微信支付的订单处理逻辑是同一套,那么我们的代码PCAliPayOrderHandler和PCWeChatOrderHandler的handle方法就是同一套处理逻辑。那么以后订单的来源可能出现为“线下”的情况,支付方式也可能出现“银行转账”、“POS机刷卡”等情况,这样发展下去,我们的OrderHandler的实现类就会慢慢膨胀起来,且代码相互重复的情况会越来越多,势必走上重构之路。
来分析一下,这种情况出现的原因究竟是什么?主要有一下三点:
- 订单来源和订单支付方式的类型越来越多。
- 订单来源和订单支付方式彼此的组合也越来越多。
- 对于不同的订单来源+支付方式的组合,可能会有相同的处理逻辑。
我们怎么解决呢?很简单,两个字:复用,相同的订单处理逻辑共用一套代码。复用很容易实现,但是,我们怎么继续维护订单来源+支付方式与订单处理器Handler的映射关系呢。我们可能会想到,改变@OrderHandlerType注解的source和payMethod类型,从String类型变成String[]数组类型。以此来维护多种订单来源+支付方式对应同一订单处理器Handler的关系。如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Service public @interface OrderHandlerType { String[] source(); String[] payMethod(); }
这样不是不能实现,但是会有以下两个大问题:
- @OrderHandlerType对应的实现类的hashCode和equals方法实现可能会变得很麻烦。
- 订单的来源和支付方式的关联关系被抹去了
订单的来源和支付方式的关联关系被抹去了。什么意思呢?例如移动端支付宝支付、移动端银行转账支付和线下银行转账支付的订单是一套处理逻辑。那么我们就会有这样一个类:
@OrderHandlerType(source = {"mobile","offline"}, payMethod = {"aliPay","bank"}) public class GeneralOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理移动端支付宝支付、移动端银行转账支付和线下银行转账支付的订单"); } }
这种情况下,GeneralOrderHandler还能表示线下支付宝支付的订单对应的处理器Handler。这显然不是我们想要的。
我们想要维护订单来源和订单支付方式之间的关联关系,如下:
@OrderHandlerType(source = "mobile", payMethod = "aliPay") @OrderHandlerType(source = "mobile", payMethod = "bank") @OrderHandlerType(source = "offline", payMethod = "bank") public class GeneralOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理移动端支付宝支付、移动端银行转账支付和线下银行转账支付的订单"); } }
在java8之前,这种方式是不行的,那如何实现我们想要的效果呢?可以新建这样一个注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited @Service public @interface OrderHandlerTypes { OrderHandlerType[] value(); }
需要注意的是,@OrderHandlerTypes这个聚合注解默认约束value来存储子注解(value换成其他将失效)。
这样,我们的GeneralOrderHandler就变成了:
@OrderHandlerTypes{ @OrderHandlerType(source = "mobile", payMethod = "aliPay"), @OrderHandlerType(source = "mobile", payMethod = "bank"), @OrderHandlerType(source = "offline", payMethod = "bank") } public class GeneralOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理移动端支付宝支付、移动端银行转账支付和线下银行转账支付的订单"); } }
在java8及之后,我们在不改变@OrderHandlerTypes的情况下,修改@OrderHandlerType,添加@Repeatable(OrderHandlerTypes.class),便可以在Handler上直接添加多个@OrderHandlerType注解了。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(OrderHandlerTypes.class) @Service public @interface OrderHandlerType { String source(); String payMethod(); }
这样,我们不仅实现了复用,同时也保留了订单来源和订单支付方式的对应关系,再也不怕业务肆意发展了。
这里额外说一句,@Repeatable仅仅是一个语法糖而已,查看
@OrderHandlerType(source = "mobile", payMethod = "aliPay") @OrderHandlerType(source = "mobile", payMethod = "bank") @OrderHandlerType(source = "offline", payMethod = "bank") public class GeneralOrderHandler implements OrderHandler { @Override public void handle(Order order) { System.out.println("处理移动端支付宝支付、移动端银行转账支付和线下银行转账支付的订单"); } }
编译后的class文件:
@OrderHandlerTypes({@OrderHandlerType( source = "mobile", payMethod = "aliPay" ), @OrderHandlerType( source = "mobile", payMethod = "bank" ), @OrderHandlerType( source = "offline", payMethod = "bank" )}) public class GeneralOrderHandler implements OrderHandler { public GeneralOrderHandler() { } public void handle(Order order) { System.out.println("处理移动端支付宝支付、移动端银行转账支付和线下银行转账支付的订单"); } }
我们发现本质上还是使用@OrderHandlerTypes({@OrderHandlerType})的方式。但是,当我们的OrderHandler上仅仅有一个@OrderHandlerType时,并不会被加上@OrderHandlerTypes,所以,我们在注入OrderHandler时要同时兼容一个和多个@OrderHandlerType的情况。
另外,不知大家注意了没有,在@OrderHandlerType中我组合了@Service注解,这样避免了开发人员在每个OrderHandler上都要加一遍@Service,同时我在@OrderHandlerTypes中也加上了@Service注解,如果@OrderHandlerTypes中没有@Service,当多个@OrderHandlerType转换成@OrderHandlerTypes之后,由于缺少@Service,会导致其不会注入到Spring容器中去。
总结
至此,基于注解干掉if-else的方式算是告一段落了。写此文的驱动主要有两点:
- 笔者不想在干掉了业务判断上的if-else之后,又在选择Handler时,通过if-else/switch获取,这样根本不叫干掉if-else。
- 之前对于注解的了解相对较少,基于注解的方式也是想加深自己对于注解的理解。
- 策略模式代替大量的if else
- Java策略模式代替大量的if else 提升代码的可拓展性 ,附带一个类似游戏王者荣耀的匹配规则的例子
- 你还在使用 if else 写代码?试试 “策略模式” 吧!
- 策略模式优化过多的if else 代码
- 小白写了一堆if-else,大神实在看不下去了,竟然用策略模式直接摆平了
- Java利用策略模式优化过多if else代码
- 业务复杂=if else?刚来的大神竟然用策略+工厂彻底干掉了他们!
- 策略模式:再见if-else
- 教你用策略模式解决多重if-else
- 使用策略模式减少if else
- 利用策略模式优化过多 if else 代码
- Android设计模式 -- 巧用策略模式告别过多的 if...else...
- 【项目实战】去除繁琐的if..else 优雅使用策略模式
- 策略模式:代替if-else-if
- 设计模式——行为型模式之借助策略模式(Strategy Pattern)减少使用不必要的if-else if -else和switch-case(三)
- 设计模式之策略模式(if..else肿瘤代码)
- 利用策略模式和工厂模式优化代码中过多的if-else
- 设计模式-策略模式Strategy以及消灭if else
- 结合策略模式和HashMap摆脱if else
- C++实现策略模式