责任链模式的定义
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
责任链模式的重点在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果,通用类图如下:
责任链通用模板图.png
代码清单:
抽象处理者
public abstract class Handler {
private Handler next;
public final Response handleMessage(Request request){
Response response = null;
if (this.getHandlerLevel().equals(request.getRequestLevel())) {
response = this.echo(request);
}else{
if (this.next != null) {
response = this.next.handleMessage(request);
}else{
//没有合适的处理者,业务自行处理
}
}
return response;
}
/**
* 设置下一个处理者是谁
* @param next
*/
public void setNext(Handler next) {
this.next = next;
}
/**
* 每个处理者都有一个处理级别
* @return
*/
protected abstract Level getHandlerLevel();
protected abstract Response echo(Request request);
}
抽象的处理者实现三个职责:
- 定义一个请求的处理方法handleMessage,唯一对外开放的方法;
- 定义一个链的编排方法setNext,设置下一个处理者
- 定义了具体的请求者必须实现的两个方法,定义自己能够处理的级别getHandlerLevel和具体的处理任务echo。
具体处理者
public class ConcreteHandler1 extends Handler{
@Override
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
@Override
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
}
public class ConcreteHandler2 extends Handler{
@Override
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
@Override
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
}
public class ConcreteHandler3 extends Handler{
@Override
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
@Override
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
}
public class Level {
//定义一个请求和处理登记
}
public class Request {
public Level getRequestLevel(){
return null;
}
}
public class Response {
//处理者返回数据
}
场景模式
public class Client {
public static void main(String[] args) {
Handler handler = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
handler.setNext(handler2);
handler2.setNext(handler3);
Response response = handler.handleMessage(new Request());
}
}
责任链模式的应用
责任链模式的优点
责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道谁处理的,处理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性。
责任链模式的缺点
责任链有两个非常显著的缺点:
- 性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。
- 调试很不方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
注意事项
链中节点数量需要控制,避免出现超长链的情况,一般做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。
责任链模式的小框架 apache commons-chain
介绍
apache commons chain的原理就是责任链模式。
maven依赖
<dependency>
<groupId>commons-chain</groupId>
<artifactId>commons-chain</artifactId>
<version>1.2</version>
</dependency>
主要类图
apache-common-chains的主要类图.png源码解析
Command接口
该接口为commons-chain组件中的核心接口,内部制只定义了一个核心方法 execute()
,对于该方法:
- 返回true(对应
Command.PROCESSING_COMPLETE
常量), 代表整体逻辑执行完毕,流程不再继续。 - 返回false(对应
Command.CONTINUE_PROCESSING
常量), 则代表**将处理逻辑交给之后的Command, 流程继续执行 **
Filter接口
该接口继承了command接口,额外添加了一个后置处理的功能boolean postprocess(Context context, Exception exception);。
Context接口
-
该接口相当于框架设计里的会话域,会在每次实际的流程处理开始前被构建。
-
其直接继承自JDK中的
Map
,这样的设计确保了足够的扩展性,使用者可以根据需要向其中填充约定的键值对,这样之后的Command实现类中可以取到这些键值对以进行自定义处理。 -
子类ContextBase不仅提供了对Map的实现而且增加了一个特性:属性-域透明 。 这个特性可以通过使用Map的put和get 方法操作JavaBean的域,当然这些域必须使用标准的getFoo和setFoo方法定义。那些通过JavaBean的“setter”方法设置的值,可以通过对应的域名称,用Map的get方法得到。同样,那些用Map的put方法设置的值可以通过JavaBean的“getter”方法得到。
具体实现原理还是通过JDK的反射实现
相关源码在ContextBase
类中的initialize()
方法private void initialize() { // Retrieve the set of property descriptors for this Context class try { //采用JDK反射 pd = Introspector.getBeanInfo (getClass()).getPropertyDescriptors(); } catch (IntrospectionException e) { pd = new PropertyDescriptor[0]; // Should never happen } // Initialize the underlying Map contents for (int i = 0; i < pd.length; i++) { String name = pd[i].getName(); // Add descriptor (ignoring getClass() and isEmpty()) if (!("class".equals(name) || "empty".equals(name))) { if (descriptors == null) { descriptors = new HashMap((pd.length - 2)); } descriptors.put(name, pd[i]); super.put(name, singleton); } } }
Chain接口
-
注意本接口中,重新声明了一次
boolean execute(Context context) throws Exception;
方法。 这个方法 规定了整个链条的处理逻辑。 -
注意其直接继承自
Command
接口,所以Chain也是一种Command,这样我们就可以将某个Chain作为Command添加到另外一个Chain中。这样我们可以以更加模块化的方式搭建出一个个代表处理逻辑的积木块(Chain
),然后将这些积木块进行拼接得到最后的完整处理链条。
execute的方法源码
public boolean execute(Context context) throws Exception {
// Verify our parameters
if (context == null) {
throw new IllegalArgumentException();
}
// Freeze the configuration of the command list
frozen = true;
// Execute the commands in this list until one returns true
// or throws an exception
boolean saveResult = false;
Exception saveException = null;
int i = 0;
int n = commands.length;
for (i = 0; i < n; i++) {
try {
//调用链条上的execute的方法
saveResult = commands[i].execute(context);
if (saveResult) {
break;
}
} catch (Exception e) {
saveException = e;
break;
}
}
// Call postprocess methods on Filters in reverse order
if (i >= n) { // Fell off the end of the chain
i--;
}
boolean handled = false;
boolean result = false;
for (int j = i; j >= 0; j--) {
//如果是Filter实现类,则调用其后置处理逻辑
if (commands[j] instanceof Filter) {
try {
result =
((Filter) commands[j]).postprocess(context,
saveException);
if (result) {
handled = true;
}
} catch (Exception e) {
// Silently ignore
}
}
}
// Return the exception or result state from the last execute()
if ((saveException != null) && !handled) {
throw saveException;
} else {
return (saveResult);
}
}
简单案例
public class Command1 implements Command {
@Override
public boolean execute(Context context) throws Exception {
Object val = context.get("key");
System.out.println("do something by command1... params is " + JSONObject.toJSONString(context));
if (val != null) {
int intVal = (Integer)val;
context.put("key", intVal+1);
}
return Command.CONTINUE_PROCESSING;
}
}
public class Command2 implements Command {
@Override
public boolean execute(Context context) throws Exception {
Object val = context.get("key");
System.out.println("do something by command2... params is " + JSONObject.toJSONString(context));
if (val != null) {
int intVal = (Integer)val;
context.put("key", intVal + 1);
}
return Command.CONTINUE_PROCESSING;
}
}
public class Command3 implements Command {
@Override
public boolean execute(Context context) throws Exception {
Object val = context.get("key");
System.out.println("do something by command3... params is " + JSONObject.toJSONString(context));
if (val != null) {
Integer intVal = (Integer)val;
context.put("key", intVal + 1);
}
return Command.CONTINUE_PROCESSING;
}
}
使用场景
public class Client {
public static void main(String[] args) throws Exception{
Context context = new ContextBase();
context.put("key", 1);
ChainBase chain = new ChainBase();
chain.addCommand(new Command1());
chain.addCommand(new Command2());
chain.addCommand(new Command3());
chain.execute(context);
}
}
场景类中的Context
类也可以替换成自己的实体类,但是需要继承ContextBase
类。
public class MyContext extends ContextBase {
private Integer key;
public Integer getKey() {
return key;
}
public void setKey(Integer key) {
this.key = key;
}
}
网友评论