为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了
优缺点
责任链模式是一种对象行为型模式,其主要优点如下。
1.降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
2.增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
3.增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
4.责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
5.责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
其主要缺点如下:
1.不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
2.对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
3.职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
场景
netty的pipeline设计,就采用了责任链设计模式, 底层采用双向链表的数据结构, 将链上的各个处理器串联起来
客户端每一个请求的到来,netty都认为,pipeline中的所有的处理器都有机会处理它,因此,对于入栈的请求,全部从头节点开始往后传播,一直传播到尾节点(来到尾节点的msg会被释放掉)
具体参看大神写的一篇文章: netty框架中的责任链模式
uml图
案例
- 抽象处理者,使用了模板模式定义好具体实现类的处理模板.并提供设置下一个处理者的方法。
public abstract class AbstractHandle {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
/** 责任链中的下一个元素*/
protected AbstractHandle nextLogger = null;
public void setNextHandle(AbstractHandle nextLogger) {
this.nextLogger = nextLogger;
}
public void handle(int level, String message) {
if (this.level <= level) {
write(message);
}
if (nextLogger != null) {
nextLogger.handle(level, message);
}
}
protected abstract void write(String message);
}
- 具体处理者,定义责任链中的执行顺序,指定下一个处理者
public class OneHandle extends AbstractHandle {
public OneHandle() {
this.level = INFO;
setNextHandle(new TwoHandle());
}
@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}
public class TwoHandle extends AbstractHandle {
public TwoHandle() {
this.level = DEBUG;
setNextHandle(new ThreeHandle());
}
@Override
protected void write(String message) {
System.out.println("File::Logger: " + message);
}
}
public class ThreeHandle extends AbstractHandle {
public ThreeHandle() {
this.level = ERROR;
}
@Override
protected void write(String message) {
System.out.println("Error Console::Logger: " + message);
}
}
- 测试程序
public class ChainPatternDemo {
public static void main(String[] args) {
AbstractHandle loggerChain = getFirstLoggers();
loggerChain.handle(AbstractHandle.INFO, "This is an information.");
loggerChain.handle(AbstractHandle.DEBUG,
"This is a debug level information.");
loggerChain.handle(AbstractHandle.ERROR,
"This is an error information.");
}
private static AbstractHandle getFirstLoggers() {
return new OneHandle();
}
}
改进
上述实现中,每个处理者均通过setNextHandle指定下一个由谁来执行,这里改进一下,通过一个类将所有职责管理起来,存在一个list中,执行时遍历该list依次执行即可.
- 定义抽象接口,该接口接收职责连管理类,目的是当前处理类处理结束后,调用下一个处理类
public interface BaseCase {
void doSomething(String input, CaseChain caseChain);
}
- 定义所有职责的管理类,保存所有职责列表,并遍历执行
public class CaseChain {
/** 所有case列表 */
private List<BaseCase> caseList = new ArrayList<>();
/** 用于遍历所有的case列表*/
int index = 0;
/** 添加case */
public CaseChain addCase(BaseCase baseCase) {
caseList.add(baseCase);
return this;
}
public void exec(String input) {
if (index == caseList.size()) {
index = 0;
//return; 当未找到职责处理时,可以从头再找,直到找到默认
}
BaseCase currentCase = caseList.get(index);
//修改索引值,以便下次回调获取下一个节点,达到遍历目的
index++;
currentCase.doSomething(input, this);
}
}
- 具体职责处理类
public class OneCase implements BaseCase {
@Override
public void doSomething(String input, CaseChain caseChain) {
if ("1".equals(input)) {
System.out.println(getClass().getName());
return;
}
caseChain.exec(input);
}
}
public class TwoCase implements BaseCase {
@Override
public void doSomething(String input,CaseChain caseChain) {
if ("2".equals(input)) {
System.out.println(getClass().getName());
return;
}
caseChain.exec(input);
}
}
public class DefaultCase implements BaseCase {
@Override
public void doSomething(String input, CaseChain caseChain) {
System.out.println(getClass().getName());
}
}
- 测试类,由客户端指定责任链顺序.
public class TestClient {
public static void main(String[] args) {
CaseChain caseChain = new CaseChain();
caseChain.addCase(new OneCase()).addCase(new TwoCase()).addCase(new DefaultCase());
caseChain.exec("0");
// 重复调用时,责任链会从上一个执行的索引开始,不会重头开始
caseChain.exec("2");
}
}