Java设计模式-责任链模式

责任链模式又称为职责链模式,在23种设计模式中归类为行为型模式。行为型模式可以分为类行为型模式和对象行为型模式。

类行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。

对象行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象复合等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用组合关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式。责任链模式就是对象行为型模式。

在责任链模式中,有多个对象都有机会处理请求,避免请求发送者与接收者耦合在一起。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

典型责任链模式

责任链一般涉及到两个角色:

  • 抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法以设定和返回对后继者的引用。这个角色通常由一个Java抽象类或者Java接口实现。
  • 具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给后继者。由于具体处理者持有对后继者的引用,因此,如果需要,具体处理者可以访问后继者。

典型抽象处理者Handler代码

public abstract class Handler {

	private Handler successor;
	
	public Handler getSuccessor() {
		return successor;
	}

	public void setSuccessor(Handler successor) {
		this.successor = successor;
	}  
	
	public abstract void handleRequest();
	
}

典型具体处理者ConcreteHandler代码

public class ConcreteHandler extends Handler {

	@Override
	public void handleRequest() {
		if (请求满足条件) {
			...//处理请求
		} else {
			getSuccessor().handleRequest();//转发给后继者
		}
	}

}

责任链模式示例

UI控件事件传递

HelpHandler类定义了一个处理帮助请求的接口。它持有对象链中后继者的引用successor,关键处逻辑handleHelp()方法可以被子类重写,hasHelp()是一个条件判断,主要用于判断是否符合当前对象的处理条件,如果满足则会执行handleHelp()方法。

public enum Topic {
	
	NONE,BUTTON,DIALOG,APPLICATION

}

public abstract class HelpHandler {

	private HelpHandler successor;
	private Topic topic;

	HelpHandler(HelpHandler successor, Topic topic) {
		this.successor = successor;
		this.topic = topic;
	}

	public void handleHelp() {
		if (successor != null) {
			successor.handleHelp();
		}
	}

	public boolean hasHelp() {
		return topic != Topic.NONE;
	}

}

所有的窗口组件都是Widget的子类,Widget是HelpHandler的子类,因为所有的界面元素都可以有帮助主题。

public abstract class Widget extends HelpHandler{

	public Widget(HelpHandler successor, Topic topic) {
		super(successor,topic);
	}
	
}
Button是链上的第一个事件处理者,由于Button也是一个窗口与组件,所以它是Widget的子类,在构造方法的入参中传入的是一个Widget,而并不是一个HelpHandler类

public class Button extends Widget {

	public Button(Widget w, Topic topic) {
		super(w, topic);
	}

	@Override
	public void handleHelp() {
		if (hasHelp()) {
			System.out.println("Button handler");
		} else {
			super.handleHelp();
		}
	}

}

Dialog类似Button的实现,只不过它的后继者不是一个Widget组件,而是一个任意的帮助请求处理对象,在本示例中它的后继者将是一个Application实例。

public class Dialog extends Widget {

	public Dialog(HelpHandler successor, Topic topic) {
		super(successor, topic);
	}

	@Override
	public void handleHelp() {
		if (hasHelp()) {
			System.out.println("Dialog handler");
		} else {
			super.handleHelp();
		}
	}

}

在链的末端是一个Application实例,该Application不是一个窗口组件。当一个帮助请求传递到这一层时,Application可提供关于该应用的一般性信息,或者它可以提供一系列不同的帮助主题。

public class Application extends HelpHandler {

	Application(HelpHandler successor, Topic topic) {
		super(successor, topic);
	}

	Application(Topic topic) {
		this(null, topic);
	}

	@Override
	public void handleHelp() {
		if (hasHelp()) {
			System.out.println("Application handler");
		} else {
			//具体逻辑操作
		}
	}

}

示例代码调用如下:

Application application = new Application(Topic.APPLICATION);
Dialog dialog = new Dialog(application, Topic.DIALOG);
Button button = new Button(dialog, Topic.BUTTON);
button.handleHelp();//Button handler

Application application = new Application(Topic.APPLICATION);
Dialog dialog = new Dialog(application, Topic.DIALOG);
Button button = new Button(dialog, Topic.NONE);
button.handleHelp();//Dialog handler

该示例的处理方式类似UI的用户事件,在Android中的事件处理也是使用的责任链模式。现在比较流行的网络请求库OkHttp,它的内部在处理网络请求时就是使用的责任链模式,在J2EE开发中的Filter也是责任链模式,可以看出在一些框架层面上面,责任链模式使用相当广泛。

仿写Servlet中Filter

有一个字符串String msg = ":):,<script>,敏感,被就业,网络授课";我们希望应用以下三个规则对字符串进行过滤和谐处理:

  • 将字符串中出现的"<>"符号替换成"[]";
  • 处理字符串中的敏感信息,将被就业和谐成就业;
  • 将字符串中出现的":):"转换成"^V^";

首先看一下定义的Request和Response以及Filter接口。

public class Request {
	
	public String request;

}
public class Response {
	
	public String response;

}
public interface Filter {

	// 定义接口Filter,具体的过滤规则需要实现这个接口
	void doFilter(Request request, Response response, FilterChain chain);

}

FilterChain的定义如下:

public class FilterChain implements Filter {

	// 用List集合来存储过滤规则
	List<Filter> filters = new ArrayList<Filter>();
	// 用于标记规则的引用顺序
	int index = 0;

	// 往规则链条中添加规则
	public FilterChain addFilter(Filter f) {
		filters.add(f);
		// 代码的设计技巧:Chain链添加过滤规则结束后返回添加后的Chain,方便我们下面doFilter函数的操作
		return this;
	}

	@Override
	public void doFilter(Request request, Response response, FilterChain chain) {
		// index初始化为0,filters.size()为3,不会执行return操作
		if (index == filters.size()) {
			return;
		}
		// 每添加一个过滤规则,index自增1
		Filter f = filters.get(index);
		index++;
		// 根据索引值获取对应的规律规则对字符串进行处理
		f.doFilter(request, response, chain);
	}

}

定义过滤处理逻辑。

public class HtmlFilter implements Filter {

	@Override
	public void doFilter(Request request, Response response, FilterChain chain) {
		request.request = request.request.replace('<', '[').replace('>', ']') +
		// 后面添加的是便于我们观察代码执行步骤的字符串
				"----HTMLFilter()";
		chain.doFilter(request, response, chain);
		response.response += "---HTMLFilter()";
	}

}
public class FaceFilter implements Filter {

	@Override
	public void doFilter(Request request, Response response, FilterChain chain) {
		// 将字符串中出现的":):"转换成"^V^";
		request.request = request.request.replace(":):", "^V^")
		// 后面添加的是便于我们观察代码执行步骤的字符串
				+ "----FaceFilter()";
		chain.doFilter(request, response, chain);
		response.response += "---FaceFilter()";
	}

}
public class SensitiveFilter implements Filter {

	@Override
	public void doFilter(Request request, Response response, FilterChain chain) {
		// 处理字符串中的敏感信息,将被就业和谐成就业
		request.request = request.request.replace("被就业", "就业")
				.replace("敏感", "") +
		// 后面添加的是便于我们观察代码执行步骤的字符串
				" ---sensitiveFilter()";
		chain.doFilter(request, response, chain);
		response.response += "---sensitiveFilter()";
	}

}

示例测试代码如下:

String msg = ":):,<script>,敏感,被就业,网络授课";
Request request = new Request();
request.request = msg;
Response response = new Response();
response.response = "response:";
// FilterChain,过滤规则形成的拦截链条
FilterChain fc = new FilterChain();
// 规则链条添加过滤规则,采用的是链式调用
fc.addFilter(new HtmlFilter())
  .addFilter(new SensitiveFilter())		
  .addFilter(new FaceFilter());
// 按照FilterChain的规则顺序,依次应用过滤规则
fc.doFilter(request, response, fc);
// 打印请求信息
System.out.println(request.request);
// 打印响应信息
System.out.println(response.response);

示例输出如下:

^V^,[script],,就业,网络授课----HTMLFilter() ---sensitiveFilter()----FaceFilter()
response:---FaceFilter()---sensitiveFilter()---HTMLFilter()

小结

职责链模式的主要优点在于可以降低系统的耦合度,简化对象的相互连接,同时增强给对象指派职责的灵活性,增加新的请求处理类也很方便;其主要缺点在于不能保证请求一定被接收,且对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

责任链可以分为纯的和不纯的责任链。一个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个:一个是承担责任,另一个是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又将责任向下传的情况。在一个纯的职责链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的职责链模式里面,一个请求可以最终不被任何接收端对象所接收。

参考资料

Java设计模式系列之责任链模式

Java设计模式之责任链模式、职责链模式

责任链模式

GOF设计模式

Java设计模式

评论

您确定要删除吗?删除之后不可恢复