Java模式之责任链模式

从请假开始谈起

话说上次五一请假回去玩了一个多星期,期间回了一次老家,去了一趟武汉,接着又跑到了景德镇,和庐山;玩的好不自在;但是想起来上次请假的经历另我记忆犹新啊,算起来也是一波三折吧;

记得那时是4月20号,因为家里出了点事情,急需回家,然后也快到五一了,就想出去玩几天,因为之前加班积攒了不少的调休,所以想把调休给用了。记得当时我是先跟我们的部门经理说,我们部门经理跟我关系比较好,我就说胡哥,我想请假,回家有点事情,随便出去玩几天,胡哥说没问题啊,随便请。然后问我请几天,我说八天。胡哥说:“八天不行啊,你要是请一天,我还能满足你,但是一天以上的你就得找我们的老大,部门总监王总了!”,我说那我去找一下王总吧,王总说,你要请八天假啊,这个我是同意的,但是我只能处理三天内的假期,然后对我说,这事你得去找一下人事部门的老大让他给你签字,然后我找到了张总,张总说请假,没问题啊,先填写请假单吧,填完了请假单之后,张总一看,嗯,没问题,不过你这个已经超过了一个星期了,你得CEO申请一下。最后我又找到了CEO,然后CEO说了一番鼓励我的话,叫我处理完事情之后早点来上班;整个请假算是告一段落了;

在阎宏博士的《Java与模式》一书中开头是这样描述责任链(Chain of Responsibility)模式的:

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

Java模式之责任链模式_第1张图片

责任链模式的结构

  下面使用了一个责任链模式的最简单的实现。

  责任链模式涉及到的角色如下所示:

  ●  抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。

  ●  具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。


什么是链:

上面讲到了我请假的流程和责任链模式的一种介绍,那么我们可以理解一下,什么是链;

1. 链是多个节点的有序集合。

2. 链的各节点可灵活拆分再重组。

再谈请假:

先创建一个Handler用来处理我们的请假流程

/*
 * 处理人,负责处理请假申请
 */
public abstract class LeaveHandler {
	/*
	 * 直接后继,用于传递请求
	 */
	protected LeaveHandler successor;

	public void setSuccessor(LeaveHandler successor) {
		this.successor = successor;
	}

	/*
	 * 处理请假申请
	 */
	public abstract void disposeLeave(int day);
}
好了现在创建我们的项目经理,他只能够处理一天的假期;如果超过了一天我们就要向他的上级传递请求

/*
 * 项目经理可以批准一天的假期
 */
public class Lead extends LeaveHandler {

	@Override
	public void disposeLeave(int day) {
		if (day <=  1) {
			System.out.println("我叫做胡经理,我可以处理" + day + "的假期");
		} else {
			// 如果他处理不了就向上传递请求
			successor.disposeLeave(day);
		}

	}

}

/*
 * 技术总监可以批准三天的假期
 */
public class CTO extends LeaveHandler{
	@Override
	public void disposeLeave(int day) {
		if (day <= 3) {
			System.out.println("我叫做王总监,我可以处理" + day + "内的假期");
		} else {
			// 如果他处理不了就向上传递请求
			successor.disposeLeave(day);
		}

	}

}

/*
 * 人事部门老大可以批准一个星期内的假期
 */
public class HrBoos extends LeaveHandler{
	@Override
	public void disposeLeave(int day) {
		if (day <= 5) {
			System.out.println("我叫做张老大,我可以处理" + day + "内的假期");
		} else {
			// 如果他处理不了就向上传递请求
			successor.disposeLeave(day);
		}

	}

}

/*
 * 老板,只要他同意,你可以无限期休假
 */
public class CEO extends LeaveHandler {
	@Override
	public void disposeLeave(int day) {
		//因为这里所有的假期他都可以处理所以没有判断
	System.out.println("我叫做CEO,我可以处理" + day + "的假期");
	}

}

public class LeaveHandlerFactory {
	
	
	/*/
	 * 创建工厂方法
	 */
	public static LeaveHandler createHandler(){
		LeaveHandler lead=new Lead();
		LeaveHandler cto=new CTO();
		LeaveHandler hrBoos=new HrBoos();
		LeaveHandler ceo=new CEO();
		
		lead.setSuccessor(cto);
		cto.setSuccessor(hrBoos);
		hrBoos.setSuccessor(ceo);
		return lead;
	}

最后编写测试用例

public class Test {
	private LeaveHandler handler;
	
	public LeaveHandler getHandler() {
		return handler;
	}

	public void setHandler(LeaveHandler handler) {
		this.handler = handler;
	}
	public void requestDiscount(int day){
		handler.disposeLeave(day);
	}
	

	public static void main(String[] args) {
		Test test=new Test();
		test.setHandler(LeaveHandlerFactory.createHandler());
		test.requestDiscount(8);
		
	}

}

责任链模式的优缺点:

模式的有点就在于实现了解耦,符合开闭原则,在这里面调用者不需要知道具体的传递过程,他只需要知道最终的结果被处理了。而且这个链表的结构可以被灵活的更改重组。

但是它的缺点也是很明显的,首先从性能上说起,一个是调用时间,如果链表在最开始被处理了还好,万一链表跑到了最后一个才被处理,那么他的调用时间肯定会比不适用责任链模式的效率要低一些;第二是内存的问题,我们会构造出很多的链表节点对象,但是有些对象在我们的应用场景中是不会用到的,所以大大的消耗了我们的内存;

纯的与不纯的责任链模式
  一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又 把责任向下传的情况。
  在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。
  纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。有些人认为不纯的责任链根本不是责任链模式,这也许是有道理的。但是在实际的系统里,纯的责任链很难找到。如果坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大意义了。
我们经常用到的责任链模式:

在早期的java中使用到责任链的有AWT这个项目;

Java模式之责任链模式_第2张图片

后来改为了观察者模式,我想这也是性能的问题;

还有我们的


Java模式之责任链模式_第3张图片

异常处理机制,我们捕获了一个异常之后可以选择自己去处理掉这个异常,也可以选择往上抛出异常;

还有前端的

Java模式之责任链模式_第4张图片

DOM树也是一样,当我们对TD绑定了一个点击事件并且触发之后,它会从这个document去寻找,如果没有找到就接着往下找,直到找到了这个TD,再返回这个点击事件;

当然还有我们比较熟悉的Filter过滤器

public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        
        chain.doFilter(request, response);
    }

    public void destroy() {
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    }

它可以自己去处理这个请求,也可以交给下一个拦截器去处理;

职责链总结

职责链模式不能乱用,否则非常容易变成因为模式而模式的反例。

如果有更好的模式可以代替我们的责任链模式的话,可以考虑去替代它。


你可能感兴趣的:(java与设计模式)