浅谈设计模式1 -- 责任链模式

        设计模式之责任链模式,在Gof的《设计模式》巨作里面是被这样定义的:
引用
        使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

        在比较容易理解的阎宏博士的《JAVA与模式》一书里面,又是这样被定义的:
引用
        责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

        其实上面两种定义大家仔细阅读后可以发现是共通的。都讲到了一根传递的链条,链条上有很多可以处理责任的对象,并且责任会随链条传递,每个对象都有机会处理这个传递中的责任,直到最终被链条中的某个对象处理掉。
        而在《设计模式》里面,给出了以下的适用范围:
引用
        1) 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。 
        2) 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。  
        3) 可处理一个请求的对象集合应被动态指定。

        我们来看看责任链模式的优缺点:
引用
          (优点)
        1.责任链模式减低了发出命令的对象和处理命令的对象之间的耦合
        2.它允许多与一个的处理者对象根据自己的逻辑来决定哪一个处理者最终处理这个命令。换言之,发出命令的对象只是把命令传给链结构的起始者,而不需要知道到底是链上的哪一个节点处理了这个命令。
        3.在处理命令上,允许系统有更多的灵活性。哪一个对象最终处理一个命令可以因为由那些对象参加责任链、以及这些对象在责任链上的位置不同而有所不同。
        (缺点)
        4.责任链模式要求链上所有的对象都继承自一个共同的父类或者实现一个共通的接口
        5.所有责任开始都是从指定的接收者依次传递下去,运行中无法从中间开始传递

        接下来我们具体来学习责任链模式,他的UML图我想在网上到处可以找到,这里就不累述了,多本设计模式的书籍里所阐述的责任链模式的角色都只有两个: 抽象处理者角色和具体处理者角色,抽象处理者只是为了让所有具体处理者继承一个共同的父类或者实现一个共同的接口。在此重要的是具体处理者,他的内部持有另外一个具体处理者的引用。我们通过如下代码来看。首先是抽象处理者角色:
public abstract class MyHandler {
	//持有责任链下一个处理者对象。
	protected MyHandler successor;
	//实际每个处理者处理责任的方法。可根据具体需要选择是否传参和传什么参数。
	public abstract void handleRequest();
	
	//以下是对MyHandler的set和get方法。以便于对处理者赋值和获取。
	public MyHandler getSuccessor() {
        return successor;
    }
	public void setSuccessor(MyHandler successor) {
        this.successor = successor;
    }
}

        接下来是具体处理者角色:
public class HandlerExecute1 extends MyHandler {

	@Override
	public void handleRequest() {
		//我们在这里简单做了一个模拟逻辑,
		//就是当下一个处理者不为空的时候我们就放弃处理而让下一个处理者来处理。
		if (getSuccessor() != null) {
			System.out.println("放过请求");
            getSuccessor().handleRequest();
		}else{
			System.out.println("处理请求");
		}
	}
}

        最后是我们的client,调用生成具体处理者并执行处理方法。
public class TestClient {

	public static void main(String[] args) {
		MyHandler h1 = new HandlerExecute1();
		MyHandler h2 = new HandlerExecute1();
		MyHandler h3 = new HandlerExecute1();
		MyHandler h4 = new HandlerExecute1();
		//Client端设置了h1有下一个处理者h2的引用
		//h2有下一个处理者h3的引用,h3有下一个处理者h4的引用
		//h4没有任何处理者的引用,根据逻辑,h4会处理对象。
		h1.setSuccessor(h2);
		h2.setSuccessor(h3);
		h3.setSuccessor(h4);
		//从h1处理者开始handle对象。
		h1.handleRequest();
	}
} 

        从上面代码中不难看出,我们在实际处理方法中没有做任何处理,仅仅是判断是否持有下一个处理者的引用,如果没有,那么自己就执行吧(打印几个字而已)。如果还持有下一个处理者引用,那么我就什么都不做,直接交给下一个处理者来办吧。谁叫我们有一个老爸呢?所以在Client代码中我们也看出来了,h1,h2,h3一个推一个,最终推到了h4头上,只有h4对象没有任何处理者的引用,倒霉催的只有自己去打印字符串了。

        其实我们还有另外一种方式实现责任链模式,相比这下,下面这种方式应该更好用。我们来看代码。下面这段代码其实也一样,定义了一个抽象处理者角色,在我们具体负责处理业务的代码中,我们传了一个参数叫WorkChain,后面我们会讲到他的用处。
public interface Work {
	public void execute(WorkChain chain);
}

        下面代码定义一个责任链对象WorkChain,他实现了抽象处理者Work接口(很奇怪吧,接口中传入这个责任链对象,责任连对象又实现这个借口,绕啊绕啊你就习惯了。)
public class WorkChain implements Work {
	//成员变量ArrayList里面储存的是所有被注册进来的具体处理者对象。
	private ArrayList<Work> workList = new ArrayList<Work>();
	//定义一个计数器,用来辅助调用下一个处理者
	int index = 0;
	
	public void execute(WorkChain chain) {
		//当计数器大于或等于具体处理者列表,直接返回,防止下标越界。
		if(index >= workList.size()) return;
		//没有越界,则从列表中拿出当前的具体处理者。
		Work work = workList.get(index);
		//下标字增加
		index++;
		//具体处理者具体调用处理方法,传入该责任链对象
		work.execute(chain);
		
	}
	
	//注册具体处理者到责任链的方法。
	public WorkChain setWork(Work work) {
		workList.add(work);
		return this;
	}
}
        上面的代码看起来有点难以理解吧,其实就是创建了一个责任链对象,将所有的具体处理者放在这个对象里面。为什么也要实现抽象处理者接口,是为了将自身也看作一个处理者对象,以便于以后的扩展。(例如将这个责任链加在另一个责任链里面作为处理者使用)。

        下面就是具体的处理者的类,三个类结合起来看可能就容易理解点了,
public class WorkA implements Work {
	
	//处理标识符,便于模拟业务逻辑。
	private String indicator;

	public void execute(WorkChain chain) {
		System.out.println("A is working now!!!!!");
		//当标识符为“executing”,则不再下发责任,直接自己处理终止。
		if ("executing".equals(indicator)) {
			System.out.println("Ok, no need to processing, I'm handling!");
		//当标识符不匹配,则还要向下一个具体处理者下发责任。
		}else{	
			//调用了责任链的execute方法,回顾责任链,是获取下一个具体处理者并执行具体方法。
			chain.execute(chain);
		}
	}
	
    //处理标识符的getter和setter方法。
	public String getIndicator() {
		return indicator;
	}
	public void setIndicator(String indicator) {
		this.indicator = indicator;
	}
}

        让我们加上测试代码来让其完整吧。
	public static void main(String[] args) {
		//实例化了四个具体处理对象ABCD
		WorkA wA = new WorkA();
		WorkB wB = new WorkB();
		WorkC wC = new WorkC();
		WorkD wD = new WorkD();
		//仅仅为C设置了可以自己处理的表示字段,
		//意味着到了D对象处理的时候,他不会下发到另一个处理者那里了
		wD.setIndicator("executing");
		//创建了一个责任链ch1,并以此绑定了A,C,B具体处理者。
		//因此此责任链的工作顺序也是A->C->B
		WorkChain ch1 = new WorkChain();
		ch1.setWork(wA).setWork(wC).setWork(wB);
		//创建了一个责任链对象ch2,并且绑定了D为具体处理者
		WorkChain ch2 = new WorkChain();
		ch2.setWork(wD);
		//将责任链ch2绑定到ch1责任链的最后,也就是责任链ch1执行后还需要执行ch2的处理者
		ch1.setWork(ch2);
		//从责任链ch1开始执行
		ch1.execute(ch1);
	}
}

         纵观以上测试代码,当我们将ABC三个具体处理者注册进了ch1这个责任链后,我们还将D这个处理者注册进了ch2这个责任链,但是我们之前说到过,责任链也是实现了抽象处理者接口的。所以也可以将责任链看作是一个特殊的处理者。所以我们当然就可以将ch2责任链对象给加到ch1后面去。

        而只具体执行逻辑是这样子的,我之前已经完成了往ch1责任链中注册处理者对象的步骤,这时候当我调用了ch1责任链的execute()执行方法的时候,他会去判断它里面的index是否大于我们注册的处理者对象个数,如果大于,就直接return了,否则,就会拿出index下标的处理者,并执行处理方法。而处理方法中又将责任链自身传了进去。而观察具体处理者代码时候,我们可以看到,当满足某个条件是,该处理者就自己处理而不下发了,而没有满足条件的时候,他就调用传入的责任链的execute方法,继续把这个责任传给index+1的那个处理者去执行...整个逻辑就是这样子的。大家仔细的咀嚼消化一下就应该搞掂了。

        而以上这个方法来实现责任链有个好处,就是他使用了接口方式来做,让Java的单一继承没有被浪费掉。而且可灵活的配置责任链并且能将多个责任链组合起来使用。

        其实java1.0AWT的事件处理机制(事件浮升机制),Tomcat的filter,还有sturts2的interceptor都是用到了责任链模式。有兴趣的童鞋可以研究一下他们的代码。

猜你喜欢

转载自goalietang.iteye.com/blog/2029214
今日推荐