责任链模式
概要
最近在写代码的时候,遇到if else 特别多,还存在嵌套情况;这导致代码很长,看着很难受,一点面向对象的感觉都没了;
也使得代码耦合度非常高;
小编通过责任链模式来解决这问题;
责任链模式定义(Chain of Responsibility)
在 GOF 23 中是这么描述的
Avoid coupling the sender of a request to its receiver by giving more then one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
译:通过为多个对象提供处理请求的机会,避免将请求的发送者耦合到其接收者。 链接接收对象并沿链传递请求,直到对象处理它。(总结:为请求创建了一个接收者对象的链。)
在这种模式中,小编个人认为有点像Node对象,只是Node里面这里逻辑判断;即请求传递给一个Node,Node不满足处理请求的条件,那么它会把相同请求传递给下一个Node,以此类推递归下去;
具体例子
例子就以我在工作中遇到的问题来阐述吧;
场景描述
往区块链发送交易修改合约时,合约会抛出一个事件,并把合约名称返回;这时中间件负责同步区块链中的交易数据,并根据合约名称来处理不同的合约数据解析转换成合约业务数据;
基本输入: 合约名称(contractName)、消息队列(blockingQueue)、http请求对象(bcClient)
过程: 根据不同合约名称,处理不同合约业务逻辑
输出: 将结果推送到队列中
coding
// 未优化前 if(CONTRACT.RECORD.equals(contractName)){ System.out.println("process contract record. 使用bcClient 发送RECORD请求,并将结果推到BlockingQueue"); }else if(CONTRACT.RECORD_NTSC.equals(contractName)){ System.out.println("process contract record_ntsc. 使用bcClient 发送RECORD_NTSC请求,并将结果推到BlockingQueue"); }else if(CONTRACT.REGISTRY.equals(contractName)){ System.out.println("process contract registry.使用bcClient 发送REGISTRY请求,并将结果推到BlockingQueue."); }else if(CONTRACT.REGISTRY_NTSC.equals(contractName)){ System.out.println("process contract registry_ntsc.使用bcClient 发送REGISTRY_NTSC请求,并将结果推到BlockingQueue."); }else if(CONTRACT.ACCOUNT.equals(contractName)){ System.out.println("process contract account. 使用bcClient 发送ACCOUNT请求,并将结果推到BlockingQueue"); }else { System.out.println("process contract default. 使用bcClient 发送default请求,并将结果推到BlockingQueue."); }
刚刚毕业的同学或许会觉得这个,这样的代码很正常不过了,但是随着业务的发展if判断可能会越来越长,非常过程过;耦合程度很高; 然后我们再仔细分析代码发现,这种方式就跟链一样一个接一个;
责任链UML设计
coding
// 使用责任链模式优化后 public AbstractContract getChainOfContract(){ AbstractContract recordContract = new RecordContract(); AbstractContract recordNTSCContract = new RecordNTSCContract(); AbstractContract registryContract = new RegistryContract(); AbstractContract registryNTSCContract = new RegistryNTSCContract(); AbstractContract accountContract = new AccountContract(); AbstractContract decaultContract = new DefaultContract(); recordContract.setNext(recordNTSCContract); recordNTSCContract.setNext(registryContract); registryContract.setNext(registryNTSCContract); registryNTSCContract.setNext(accountContract); accountContract.setNext(decaultContract); return accountContract; } public static void main(String[] args){ String contractName = args[0]; AbstractContract contract = getChainOfContract(); contract.exectue(contractName); }
这样一写是不是很清晰了;
然后我们再来分析一下,使用责任链模式之前与之后到底有什么区别,有些人可能会认为代码也没少多少,还要创建很多的类对象,反而变得复杂化了;
如果有这种观点的话,说明那些人还没真正理解面向对象设计;
责任链(getChainOfContract()函数)上的处理者处理请求(即AbstractContract 类),而客户端(main 函数,发送者)只需将请求发送到责任链上即可,不需要关心请求的处理细节和请求的传递,所以责任链模式将请求的发送者和请求的处理者解耦;
- 使过程化且繁琐的判断代码,转化成面向对象设计去实现,是整个逻辑思维更符合面向对象,并放映真实世界的事物映射,而不仅仅是一段逻辑代码程序
优点
- 降低耦合度。它将请求发送者与请求处理者解耦
- 灵活使用对面对象设计思想,使得对象不需要链的结构,而是使用Next方式传递请求
- 可以自由组织对象职责关系、顺序、也可以删除,增强给对象指派职责(请求)的灵活性
- 增加新的请求处理类很方便
缺点
- 不能保证请求一定被接收
- 创建类多了,占内存,影响性能
- 不容易调试
应用场景例子
那么平时我们生活中或者开源框架中有哪些是用例责任链模式来处理的呢?
例如:
- Spring MVC中的 Filter
- 击鼓传花游戏
- 平时最常用的Log日志,info、debug、error
- JS中的冒泡事件
PS:个人建议写博客类文档时尽量使用MarkDown,等写完之后在复制过来; MarkDown 程序员的记事本