您的位置:首页 > 其它

注解实现策略模式干掉if-else(下)

2020-07-13 04:21 183 查看

上篇回顾

上一篇(注解实现策略模式干掉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的实现类就会慢慢膨胀起来,且代码相互重复的情况会越来越多,势必走上重构之路。

来分析一下,这种情况出现的原因究竟是什么?主要有一下三点:

  1. 订单来源和订单支付方式的类型越来越多。
  2. 订单来源和订单支付方式彼此的组合也越来越多。
  3. 对于不同的订单来源+支付方式的组合,可能会有相同的处理逻辑。

我们怎么解决呢?很简单,两个字:复用,相同的订单处理逻辑共用一套代码。复用很容易实现,但是,我们怎么继续维护订单来源+支付方式与订单处理器Handler的映射关系呢。我们可能会想到,改变@OrderHandlerType注解的source和payMethod类型,从String类型变成String[]数组类型。以此来维护多种订单来源+支付方式对应同一订单处理器Handler的关系。如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface OrderHandlerType {
String[] source();
String[] payMethod();
}

这样不是不能实现,但是会有以下两个大问题:

  1. @OrderHandlerType对应的实现类的hashCode和equals方法实现可能会变得很麻烦。
  2. 订单的来源和支付方式的关联关系被抹去了

订单的来源和支付方式的关联关系被抹去了。什么意思呢?例如移动端支付宝支付、移动端银行转账支付和线下银行转账支付的订单是一套处理逻辑。那么我们就会有这样一个类:

@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的方式算是告一段落了。写此文的驱动主要有两点:

  1. 笔者不想在干掉了业务判断上的if-else之后,又在选择Handler时,通过if-else/switch获取,这样根本不叫干掉if-else。
  2. 之前对于注解的了解相对较少,基于注解的方式也是想加深自己对于注解的理解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: