策略模式实现的方式也大同小异:主要是定义统一行为(接口或抽象类),并实现不同策略下的处理逻辑(对应实现类)。客户端使用时自己选择相应的处理类,利用工厂或其他方式
实体类
模拟订单类
@Datapublic class Order {/*** 订单来源*/private String source;/*** 支付方式*/private String payMethod;/*** 订单编号*/private String code;/*** 订单金额*/private BigDecimal amount;// ...其他的一些字段}
业务类
假如对于不同来源(pc端、移动端)的订单需要不同的逻辑处理。
项目中一般会有OrderService这样一个类
如下,里面有一坨if-else的逻辑,目的是根据订单的来源的做不同的处理
1、传统方式
@Servicepublic class OrderService {public void orderService(Order order) {if(order.getSource().equals("pc")){// 处理pc端订单的逻辑}else if(order.getSource().equals("mobile")){// 处理移动端订单的逻辑}else {// 其他逻辑}}}
2、策略模式
1.定义处理类OrderHandler
public interface OrderHandler {void handle(Order order);}
2.定义一个OrderHandlerType注解
来表示某个类是用来处理何种来源的订单
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Servicepublic @interface OrderHandlerType {String source();}
3.实现
接下来就是实现pc端和移动端订单处理各自的handler,并加上我们所定义的OrderHandlerType注解
- 移动端处理
```java
@OrderHandlerType(source = “mobile”)
public class MobileOrderHandler implements OrderHandler {
@Override
public void handle(Order order) {
} }System.out.println("处理移动端订单");
- pc端处理```java@OrderHandlerType(source = "pc")public class PCOrderHandler implements OrderHandler {@Overridepublic void handle(Order order) {System.out.println("处理PC端订单");}}
4.使用
以上准备就绪后,就是向spring容器中注入各种订单处理的handler,
并在OrderService.orderService方法中,通过策略(订单来源)去决定选择哪一个OrderHandler去处理订单。
@Servicepublic class OrderService {private Map<String, OrderHandler> orderHandleMap;@Autowiredpublic void setOrderHandleMap(List<OrderHandler> orderHandlers) {// 注入各种类型的订单处理类orderHandleMap = orderHandlers.stream().collect(Collectors.toMap(orderHandler -> AnnotationUtils.findAnnotation(orderHandler.getClass(), OrderHandlerType.class).source(),v -> v, (v1, v2) -> v1));}public void orderService(Order order) {// ...一些前置处理// 通过订单来源确定对应的handlerOrderHandler orderHandler = orderHandleMap.get(order.getSource());orderHandler.handle(order);// ...一些后置处理}}
在OrderService中,维护了一个orderHandleMap
它的key为订单来源,value为对应的订单处理器Handler。
通过@Autowired去初始化orderHandleMap。
这样一来,OrderService.orderService里的一坨if-else不见了,取而代之的仅仅是两行代码。
即,先从orderHandleMap中根据订单来源获取对应的OrderHandler,然后执行OrderHandler.handle方法即可。
这种做法的好处是,不论以后业务如何发展致使订单来源种类增加,OrderService的核心逻辑不会改变,我们只需要实现新增来源的OrderHandler即可,且团队中每人开发各自负责的订单来源对应的OrderHandler即可,彼此间互不干扰
优化
现在回过头看orderHandleMap这个Map,它的key是订单来源,
假如,我们想通过订单来源+订单支付方式这两个属性来决定到底使用哪一种OrderHandler怎么办?
比如PC端支付宝支付的订单是一种处理逻辑(PCAliPayOrderHandler),
PC端微信支付的订单是另外一种处理逻辑(PCWeChatOrderHandler),
对应的还有移动端支付宝支付(MobileAliPayOrderHandler)和移动端微信支付(MobileWeChatOrderHandler)
这时候我们的key应该存什么呢,可能有人会说,我直接存订单来源+订单支付方式组成的字符串不就行了吗?
确实可以,但是如果这时业务逻辑又变了(向pm低头),
PC端支付宝支付和微信支付是同一种处理逻辑,而移动端支付宝支付和微信支付是不同的处理逻辑,
那情况就变成了PCAliPayOrderHandler和PCWeChatOrderHandler这两个类是同一套代码逻辑。
我们干掉了if-else,但却造出了两份相同的代码
查看注解的定义和反向代理,可以得知
为什么不把key的类型设置为OrderHandlerType?就像这样。private Map<OrderHandlerType, OrderHandler> orderHandleMap;
如此一来,不管决定订单处理器orderhandler的因素怎么变,我们便可以以不变应万变,如下
public class OrderService {private Map<OrderHandlerType, OrderHandler> orderHandleMap;@Autowiredpublic void setOrderHandleMap(List<OrderHandler> orderHandlers) {// 注入各种类型的订单处理类orderHandleMap = orderHandlers.stream().collect(Collectors.toMap(orderHandler -> AnnotationUtils.findAnnotation(orderHandler.getClass(), OrderHandlerType.class),v -> v, (v1, v2) -> v1));}// ...省略}
我们怎么根据order的来源和支付方式去orderHandleMap里获取对应的OrderHandler呢?
问题变成了如何关联order的来源和支付方式与OrderHandlerType注解。
还记得刚才所说的注解就是个接口吗,既然是个接口,我们自己实现一个类不就完事了么,
这样就把order的来源和支付方式与OrderHandlerType注解关联起来了。说干就干,现在我们有了这么一个类,
public class OrderHandlerTypeImpl implements OrderHandlerType {private String source;private String payMethod;OrderHandlerTypeImpl(String source, String payMethod) {this.source = source;this.payMethod = payMethod;}@Overridepublic String source() {return source;}@Overridepublic String payMethod() {return payMethod;}@Overridepublic Class<? extends Annotation> annotationType() {return OrderHandlerType.class;}@Overridepublic int hashCode() {int hashCode = 0;hashCode += (127 * "source".hashCode()) ^ source.hashCode();hashCode += (127 * "payMethod".hashCode()) ^ payMethod.hashCode();return hashCode;}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof OrderHandlerType)) {return false;}OrderHandlerType other = (OrderHandlerType) obj;return source.equals(other.source()) && payMethod.equals(other.payMethod());}}
具体的业务逻辑如下:
public void orderService(Order order) {// ...一些前置处理// 通过订单来源确以及支付方式获取对应的handlerOrderHandlerType orderHandlerType = new OrderHandlerTypeImpl(order.getSource(), order.getPayMethod());OrderHandler orderHandler = orderHandleMap.get(orderHandlerType);orderHandler.handle(order);// ...一些后置处理}
这样以来,不管以后业务怎么发展,OrderService核心逻辑不会改变,只需要扩展OrderHandler即可
