最近认真学了一下设计模式的责任链模式,觉得把它用在项目的优化上,应该可以使代码变得更加优雅。
定义
使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。将所有处理者形成一条链,在链中决定哪个对象能够处理请求,并返回结果,不能处理则继续向下传递请求。
组成
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
优点
将请求和处理分开,请求者不需要知道是谁处理的,处理者可以不用知道请求的全貌。
缺点
- 性能问题,每个请求从链头遍历到链尾,如果链比较长则性能低下。
- 调试问题,属于递归调用,调试不方便。
使用场景
在我的药源精鉴项目中,像一些需要过期处理的东西,比如,订单未支付过期处理,发红包未领取自动退还,代金券未支付过期处理等。可以采用的一种方式就是在存入redis的时候,会通过key来区分是什么业务,比如订单的key为AA开头,红包的key为BB开头,代金券的key为CC开头。那么,如果监听到redis中有过期的键值对的话,程序会得到键值对的key,通过key来判断属于哪部分业务,然后进行相应的处理。传统做法为,将所有的处理业务都写在一个类,或者一个方法中,通过多个if判断执行不同的逻辑,比如,如果是AA开头,那么执行订单的过期处理逻辑等等。
这种做法虽然没有问题,但是会使处理逻辑耦合在一起,变得比较臃肿,复杂,难以维护。而大部分设计模式都是为了增加程序的可维护性以及可扩展性,或者说解耦。
我想了想,在这个场景中,就可以应用责任链模式,相当于我把每一个处理逻辑都看作是一个独立的处理者,然后将他们形成一个处理链,这样当一个请求进来之后,通过遍历这个处理链来处理请求。在遍历链时,通过判断,如果能处理该请求,则处理并返回,不再继续向下遍历,如果不能处理则继续向下遍历,交给下一个处理者。
具体代码实现
通过上面的描述,我们知道,所有的处理者都是独立的,并且每个处理者应该都具备相同的行为—-处理过期逻辑,接下来就可以用责任链模式那一套了,首先我通过一个抽象类,定义出抽象的处理者。
public abstract class AbstractRedisExpireHandle { private String prefix; public AbstractRedisExpireHandle(){}
public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } abstract void expireHandle(String redisKey); }
|
接下来看具体的处理者,所有的具体处理者都要继承抽象处理者,具体处理者之间是完全独立的,解耦的。
@Component public class GoodsOrderExpireHandle extends AbstractRedisExpireHandle { private static final String PREFIX = "dd"; public GoodsOrderExpireHandle(){ setPrefix(PREFIX); } @Reference(version = "1.0.0") private TaskService taskService; @Override void expireHandle(String redisKey) { taskService.goodsOrderExpireHandle(redisKey); } }
@Component public class RedPacketExpireHandle extends AbstractRedisExpireHandle { private static final String PREFIX = "kb"; @Reference(version = "1.0.0") private TaskService taskService; public RedPacketExpireHandle(){ setPrefix(PREFIX); } @Override void expireHandle(String redisKey) { taskService.redPacketExpireHandle(redisKey); } }
|
所有的具体处理者定义完后,需要将他们组成一个处理链,并且进行工作,接下来看如何将所有的相关具体处理者组成一个处理链,因为项目是基于spring的,所以这里使用了spring提供的几个相关接口,并且在spring容器启动的时候完成处理链的创建。
@Component public class ExecuteHandle implements ApplicationContextAware,InitializingBean { private ApplicationContext context; private List<AbstractRedisExpireHandle> expireHandles = new ArrayList<>(); @Override public void afterPropertiesSet() throws Exception { String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,AbstractRedisExpireHandle.class); for(int i=0;i<beanNames.length;i++){ expireHandles.add((AbstractRedisExpireHandle)context.getBean(beanNames[i])); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } public void handle(String redisKey){ if(expireHandles.size() > 0){ for(AbstractRedisExpireHandle abstractRedisExpireHandle : expireHandles){ if(redisKey.startsWith(abstractRedisExpireHandle.getPrefix())){ abstractRedisExpireHandle.expireHandle(redisKey); } } } } }
|
上述代码比较简单,该类实现了spring的两个接口,一个是ApplicationContextAware和InitializingBean。
ApplicationContextAware为容器感知接口,实现这个接口需要实现setApplicationContext方法,spring会将容器当做参数传递到这个方法中,我们就可以使用当前容器了。
InitializingBean接口为初始化Bean的接口,实现这个接口需要实现afterPropertiesSet方法,该方法会在spring实例化这个类的时候被调用,可以做一些初始化工作,我就是在这个方法中将所有的具体处理者形成处理链的。
最后,通过遍历整个处理链,来处理相关的逻辑,结合之前所写的文章,使用该处理链处理请求的代码也十分简单,只需要调用上面的handle方法即可。
@Component public class RedisExpireListener implements MessageListener { @Autowired private ExecuteHandle executeHandle; @Override public void onMessage(Message message, byte[] bytes) { byte[] body = message.getBody(); String redisKey = new String(body); executeHandle.handle(redisKey); } }
|
这样做之后,我们将所有具体处理者都进行了解耦,互相之间毫无关系。那么后续如果在有新的处理逻辑需要加入进来的话,只需要继承抽象处理者,实现相关的处理逻辑,具体处理者就可以进行工作了,无需修改其他任何代码。