对于一般的程序员而言,在平时的coding当中并没有刻意去应用什么模式。但最近我的一个体会就是,当你发现已完成功能的实现代码,其侵入性非常之高;再从软件产品整体的角度去俯瞰,该部分代码甚至已经完全不符合OCP、SRP等原则了,需求稍有变更,就需要有伤筋动骨的改动,从而导致开发的工作量变得繁多而复杂。
这就是软件设计和实现不合理所造成的困扰。其实际来源于是我最近做的一个银行业务软件产品,具体情形可概述为:当一个web请求到达的时候,去执行逻辑1,得到结果1;再根据结果1,执行逻辑2,得到结果2,……依次执行,最后得到结果N,也就是最终结果。
就是这个流程,写在了一个主方法中,从逻辑1至逻辑N,依次执行。都封装在一个方法里,对于主类,其职责不单一;其次,改动也会影响主类,代码体验相当不好。其实解决耦合度的难点在于执行逻辑2是依赖或者不依赖逻辑1的执行结果的,而且从逻辑1到逻辑N,会在其中的某个逻辑返回结果。
责任链模式刚好能够解决这个场景。启发来源于struts2/webwork的Interceptor:就struts2的Action的职责层面而言,需要在Action这个层面上完成的职责很多,因此通过Interceptor将职责进行合理的分类和排序,将他们组织成有序的执行队列。struts2中使用了一种类似责任链的设计模式对这些不同的职责进行分类并串联起来,从而使得Action层具备了丰富的层次结构。而在这个执行队列中的每个元素,就被我们称之为Interceptor,也就是拦截器。
而具体到这个软件产品中,将具体要执行的逻辑1,逻辑2,……逻辑n视为command,用一个命令链给它们穿起来。但是还需要提供每一个command执行之后的上下文context。而上述需求,适用org.apache.commons.chain,就能够完全实现。
主类如下:
public class Tx102010{
public String process(Document document) throws CodeException {
org.apache.commons.chain.Command command = new Tx102010Chain(this.getTxCode());
// 先将需要的信息放入context中
org.apache.commons.chain.Context ctx= new ContextBase();
ctx.put("TxCode",this.getTxCode());
ctx.put("RiskLevel", riskLevel);
ctx.put("Activity", activity);
ctx.put("DbTableName", dbTableName);
// 执行命令链
command.execute(ctx);
}
}
命令链Tx102010Chain将各种command加载到链中:
public class Tx102010Chain extends ChainBase {
public Tx102010Chain(String txCode){
this.txCode = txCode;
// 通过配置文件配置的具体command,将这些commmand加载到系统环境变量内存map中
String commands = SystemEnvironment.txCommandMap.get(this.getTxCode());
if(commands!=null){
String[] commandArray = commands.split(",");
if(commandArray != null){
for(int index=0; index<commandArray.length; ++index){
// addCommand方法是将具体命令加载到链中,实现了ChainBase
org.apache.commons.chain.impl.ChainBase.addCommand((TxBaseCommand)SystemEnvironment.APPLICATION_CONTEXT.getBean(commandArray[index]));
}
}
}
}
}
具体命令执行的业务逻辑举例如下:
public class AccountGrayCommand implements Command {
public boolean execute(Context ctx) throws Exception {
String txCode = (String) ctx.get("TxCode");
String frontCode = txCode == null ? null : txCode.trim().substring(0, 4);
Activity activity = (EbankTransfer) ctx.get("Activity");
boolean isAccountGray = this.txDAO.queryAccountGrayList(activity);
if (isAccountGray) {
ctx.put("RiskLevel", SysEnv.RISK_LEVEL_HIGH);
return true;//返回不执行命令
}
return false;//继续执行下一个命令
}
}
责任链模式,只是一根绳子,它不能够处理并行的请求。
责任链模式教学代码如下:
public abstract class Handler {
protected Handler successor;
//处理请求,由子类完成
public abstract void handleRequest(String request);
//设置下一个处理请求的人
public void setNextHandler(Handler successor) {
this.successor = successor;
}
}
public class LaoShi extends Handler{
public void handleRequest(String request) {
if ("请假不去上课".equals(request)) {
System.out.println(name + "可以处理" + request + ",给予批准!");
} else {
System.out.println(name + "不可以处理" + request + "转交给"
+ successor.getName());
successor.handleRequest(request);
}
}
}
public class BanZhang extends Handler {
public void handleRequest(String request) {
if ("不去开班会".equals(request)) {
System.out.println(name + "可以处理" + request + ",给予批准!");
} else {
System.out.println(name + "不可以处理" + request + "转交给"
+ successor.getName());
successor.handleRequest(request);
}
}
}
public class CopPattern {
public static void main(String[] args) {
Handler banzhang = new BanZhang("班长");
Handler laoshi = new BanZhang("老师");
banzhang.setNextHandler(laoshi); // 设置班长的下一个处理者是老师
laoshi.setNextHandler(daoyuan);// 设置老师的下一个处理者是导员
String requests = "退学";
// 把请求交给班长即可,如果班长处理不了会一层层往上交
banzhang.handleRequest(requests);
}
}