004-行为型-05-职责链模式(Chain of Responsibility)
一、概述
为请求创建一个接收此次请求对象的链
该模式构造一系列分别担当不同的职责的类的对象来共同完成一个任务,这些类的对象之间像链条一样紧密相连,所以被称作职责链模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
其有两种形式,一种是通过外部调用的方式对链的各个节点调用进行控制,从而进行链的各个节点之间的切换。另一种是链的每个节点自由控制是否继续往下传递链的进度,这种比较典型的使用方式就是Netty中的责任链模式。
1.1、简单方式
链的每个节点只需要专注于各自的逻辑即可,而当前节点调用完成之后是否继续调用下一个节点,这个则由外部控制逻辑进行。
1.1.1、示例
在执行某个任务时,其需要经过诸如时效性检验,风控拦截,任务完成次数等过滤条件的检验之后才能判断当前任务是否能够执行,只有在所有的过滤条件都完成之后,我们才能执行该任务。那么这里我们就可以抽象出一个Filter
接口,其设计如下:
com.github.bjlhx15.patterns.base.eg03action.eg05chain.eg01outtercontroller包下:
a、过滤接口设计:
public interface Filter { /** * 用于对各个任务节点进行过滤 */ boolean filter(Task task); }
Filter.filter()
方法只有一个参数Task
,主要就是控制当前task是否需要被过滤掉,其有一个boolean类型的返回值,通过该返回值以告知外部控制逻辑是否需要将该task过滤掉。
b、子类设计
public class DurationFilter implements Filter { @Override public boolean filter(Task task) { System.out.println("时效性检验"); return true; } }
public class RiskFilter implements Filter { @Override public boolean filter(Task task) { System.out.println("风控拦截"); return true; } }
public class TimesFilter implements Filter { @Override public boolean filter(Task task) { System.out.println("次数限制检验"); return true; } }
模拟声明了三个Filter
的子类,用于设计一系列的控制当前task是否需要被过滤的逻辑
c、调用控制
public class FilterTest { private List<Filter> filters; @Before public void setUp() throws Exception { filters=new ArrayList<>(); filters.add(new DurationFilter()); filters.add(new RiskFilter()); filters.add(new TimesFilter()); } @Test public void filter() { Task task = new Task(); // 这里task一般是通过数据库查询得到的 for (Filter filter : filters) { if (!filter.filter(task)) { return; } } // 过滤完成,后续是执行任务的逻辑 } }
控制比较简单,只需要实现一个统一的接口即可,其基本上能够满足大部分的逻辑控制,但是对于某些需要动态调整链的需求其就无能为力了。
1.2、节点控制模式
1.2.1、适用场景
一个请求的处理需要多个对象当中的一个或几个协作处理
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
1.2、优缺点
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
1.3、类图角色及其职责
职责链模式的角色和职责
1、抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
1.4、演进过程
创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
创建抽象的记录器类。
public abstract class AbstractLogger { public static int DEBUG = 1; public static int INFO = 2; public static int ERROR = 3; protected int level; //责任链中的下一个元素 protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger) { this.nextLogger = nextLogger; } public void logMessage(int level, String message) { if (this.level <= level) { write(message); } if (nextLogger != null) { nextLogger.logMessage(level, message); } } abstract protected void write(String message); }
创建扩展了该记录器类的实体类。
ConsoleLogger
public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard Console::Logger: " + message); } }
ErrorLogger
public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Error Console::Logger: " + message); } }
FileLogger
public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }
创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。
private AbstractLogger getChainOfLoggers() { AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.INFO); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.DEBUG); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; }
测试
@Test public void logMessage() { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information."); System.out.println("----------"); loggerChain.logMessage(AbstractLogger.INFO, "This is an information."); System.out.println("----------"); loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information."); }
输出
Standard Console::Logger: This is a debug level information. ---------- File::Logger: This is an information. Standard Console::Logger: This is an information. ---------- Error Console::Logger: This is an error information. File::Logger: This is an error information. Standard Console::Logger: This is an error information.
二、扩展
应用例子:struts的拦截器、servlet的过滤器、Dubbo中的Filter、Mybatis中的Plugin
javax.servlet.Filter的doFilter、FilterChain
SpringSecurity