之前我们已经将前两种类型的设计模式都已经悉数介绍完呢(创建型和结构型),希望此时大家能够对它们有一个全新的认识,不仅加深对各个模式的理论理解,更需要在日常开发活动中实践之,这样才能真正做到对模式的融会贯通。模式的学习和领悟需要一个过程,不可能一蹴而就,更不可高开低走,需要大家静下心来扎实地一步步学习和提高。从今天开始,我们将继续完成最后一种类型的设计模式——行为型模式的介绍和学习,所谓的行为型模式实际上涉及到算法和对象间职责的分配问题,行为型模式不仅是描述对象或者类的模式,还描述它们之间的通信模式。这些模式扁鹊了在运行时刻难以跟踪的复杂控制流,将你的注意力从控制流转移到对象间的联系方式上来。同时行为型模式可分为行为类模式和行为对象模式两种,前者是使用继承机制在类间分派行为,后者是使用对象组合的方式来完成行为的分派。本文将对第一个行为型模式——职责链模式进行深入的介绍和学习,开始我们对行为型模式的探索之旅吧!
动机在实际的软件系统开发中,有时我们会面临需要将操作请求转发到一系列接收者对象组成的链式结构情况,之所以是一系列接收者而不是单个接收者是因为操作请求者并不知道清楚当前请求将交由具体某个接收者来完成,而每个接收者能够处理的请求操作是有限度的,不可能面面俱到。面对一特定操作请求,它要么能够完全处理,完成对操作请求的相关任务,要么就只能向下传递给它认为可以胜任当前操作请求的下一个请求接收者对象,由其继续完成当前操作请求任务。试想一下,如果我们事先不能构建这样一条接收请求对象链,那么在客户端我们将不得不直接与处理特定请求的接收者打交道,这不仅增加了用户程序对请求接收者的使用难度,而且也势必造成两者的紧耦合关系。上述场景便是职责链模式擅长之地,在职责链模式里,若干对象由每一个对象对其下家的引用而连接起来形成请求处理链,请求将从该链头依次向链尾传递,直到遇到能够完全处理当前请求的接收者对象,此时才会结束请求向下传递过程。而请求的发出者并不知道当前链中哪个对象能够处理当前请求,这样系统就可以在不影响客户端的情况下动态地重新组织和分配职责,从而也就将具体操作请求与系列请求接收者解耦开来。接下来,让我们深入地学习职责链模式,尽量做到了然于胸吧。 意图使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 结构图
抽象处理者(Handler)角色:定义职责的接口,也就是在这里定义处理请求的方法,亦可以在这里实现后继链。 具体处理者(ConcreteHandler)角色:实现职责的类,在这个类中,实现对在它职责范围内请求的处理,如果处理不了,就继续转发请求给后继者。 客户端(client)角色:职责链的客户端,向链上的具体处理驍提交请求,让职责链负责处理。 代码示例1: public abstract class Handler{ 2: protected Handler successor; 3: public void SetSuccessor(Handler handler){ 4: this.successor=successor; 5: }
6:
7: public abstract void handleRequest(String request); 8: }
9:
10: public class ConcreteHandler1 extends Handler{ 11: public void handleRequest(String request){ 12:
13: //需要某些条件来判断当前请求是否为自己所能处理的范围,这里只是简单说明下 14: boolean someCondition=false; 15: if(someCondition){ 16: System.out.println("ConcreteHandler1 handle request:"+request); 17: }else { 18: if(successor!=null){ 19: //既然当前处理器不能处理请求,那么就直接将当前请求传递给它直接后继处理器,由其继续处理 20: successor.handleRequest(request); 21: }
22: }
23: }
24: }
25:
26: public class ConcreteHandler2 extends Handler{ 27: public void handleRequest(String request){ 28: boolean someCondition=false; 29: if(someCondition){ 30: System.out.println("ConcreteHandler2 handle request:"+request); 31: }else { 32: if(successor!=null){ 33: successor.handleRequest(request); 34: }else { 35: System.out.println("当前请求不能被所有处理者处理!"); 36: }
37: }
38: }
39: }
40:
41: public class Client{ 42: public static void main(String[] args){ 43: ConcreteHandler1 handler1=new ConcreteHandler1(); 44: ConcreteHandler2 handler2=new ConcreteHandler2(); 45:
46: handler1.SetSuccessor(handler2);
47: handler2.SetSuccessor(null); 48:
49: handler1.handleRequest("just for test"); 50: }
51: }
从示例代码中,我们可以大致清楚地明白职责链模式的实现方式,如果大家还记得之前我们介绍过的设计模式,应该对这段代码会感觉似曾相识,还记得装饰模式的示例程序吗?首先,我们通过定义完成职责的统一接口,即Handler类,在其中,我们不仅定义了完成职责的接口,而且也保存着其后继处理器引用句柄,目的就是当当前处理器无法处理当前请求操作时,交给这个后继处理器来处理,完成对请求传递过程,而不是停止于自己,让有能力负责的处理器来完成对请求的处理工作。而具体的处理器ConcreteHandler1和ConcreteHandler2都是实现了自己处理请求的业务逻辑功能,也就是hanlerRequest方法。而在客户端,我们根据业务需求将不同的具体处理器形成链式结构,然后直接将操作请求放置到处理链上进行处理即可,这样一来,客户端只需要知道职责链的第一个处理对象即可,因为需要通过它将操作请求传递到职责链中。当然,客户端也并不知道当前请求将由哪一个具体处理器接受处理,理想情况是不需要知道的。在这里,需要提醒一点是,虽然职责链在示例代码中是由客户端直接构建生成的,但是也完全可以由不同的情景上下文对象根据实际的业务要求来生成不同处理能力的职责链,直接交由客户端使用,而不需要客户端负责对职责链的创建工作。 在实际的职责链模式实现里,请求不一定会被处理,因为可能没有合适的处理者,请求在职责链中从头传到尾,每个处理对象判断不属于自己处理,最后请求就没有对象来处理,这一点需要明白。再者就是,在职责链模式里有纯与不不纯之说,所谓“纯”职责链模式,就是对于链上的每一个处理对象,要么能有能力直接处理请求,结束请求在职责链上的传递,要么就直接将请求原封不动地直接传递其后继处理对象处理,而不能既对请求做部分操作又将部分处理过的请求传递给后继处理对象。但是话虽如此,在现实的应用场景中,很难出现如此“纯”的职责链模式,所以还是只能看到“不纯”的职责链实现。 现实场景在现实的生活场景中,其实也不乏职责链模式原型的例子。比如企业里的费用报销活动就是一个比较鲜活的实例,一般来说,对于较小金额的费用报销通过自己直系领导就可以获得批准报销,但是当报销费用金额变大时,直系领导就无权做决定呢,需要将该报销请求传递到其直系领导上呢,就这样,随着报销费用金额的不断加大,报销请求也就必将会依次传递到具有对当前报销费用做决定的领导身上。当然,在这一链上的任一领导都有权利直接否决该报销请求,也就是不将该报销请求继续往其直系领导传递呢,这也是真实场景可以发生的事情。但是不管最终哪一层级的领导有能力处理当前报销请求,作为报销人一开始只能将该报销请求传递给其直系领导,然后由他来决定是否需要将请求传递到更加层级的领导中,否则就是越级上报呢,现实中一般是不允许出现这样的操作流程的:)。说到这里,结合我们刚刚对职责链模式的介绍和示例代码,大家应该能够将上述场景出现的人、物与职责链模式中出现的角色一一对应了。首先,报销请求者就是client,而报销的费用就是request,而各个层级的领导就是各个具体的hanler对象,而企业中规定好各个层级领导所应具有的职权范畴,同时各个层级领导之间的上下级关系也是早已规定确定下来,也就是说自然形成了一个无形的职责链。下面我们就通过代码来演绎下上述场景吧! 1: public abstract class Leader{ 2: protected Leader successor; 3: public void SetSuccessor(Leader handler){ 4: this.successor=successor; 5: }
6:
7: public abstract void handleRequest(long fee); 8: }
9:
10: public class Manager extends Leader{ 11: public void handleRequest(long fee){ 12: if(fee<1000){ 13: //当报销费用小于一千时,经理有权力来决定批准不批准,而无需上报上级领导 14: //下面只是简单示意一下,现在情况下是可以批或者不批:) 15: System.out.println("批准或者不批准当前报销费用:"+fee); 16: }else { 17: if(successor!=null){ 18: //如果当前报销费用金额已经超过经理能够处理的范围应该直接将该报销请求传递到后继领导进行处理, 19: //在这里,也就是总经理呢。 20: successor.handleRequest(fee); 21: }
22: }
23: }
24: }
25:
26: public class President extends Leader{ 27: public void handleRequest(long fee){ 28: if(fee<10000){ 29: //当报销费用小于一万时,总经理有权力来决定批准不批准 30: //下面只是简单示意一下,现在情况下是可以批或者不批:) 31: System.out.println("批准或者不批准当前报销费用:"+fee); 32: }else { 33: System.out.println("超过公司报销费用上限,直接否决:)"); 34: }
35: }
36: }
37: }
38:
39: public class Client{ 40: public static void main(String[] args){ 41: Manager manager=new Manager(); 42: President president=new President(); 43:
44: manager.SetSuccessor(president);
45: president.SetSuccessor(null); 46:
47: manager.handleRequest(500);
48: manager.handleRequest(5000);
49: }
50: }
通过结构图来表示如下:
由经理和总经理构成的链式结构简单表示如下:
通过上图能直观地说明了经理是职责链的入口点,而总经理是职责链的最后处理者,如果它都无法处理,那该请求也只能原样返回或者直接废弃呢。但是对费用报销者来说,他并不清楚当前报销费用请求最终将被哪一层级领导处理,因为他本身并不需要知道哪一层级领导具有什么职能,并且现实情况下,他也只能将报销请求交由其直系领导来处理,而不能越级上报:) 另外,熟悉java web开发的朋友对jsp 中的过滤器Filter肯定不会陌生,我们可以定义多个不同功能的过滤器,在web.xml文件中配置和确定各个过滤器的执行过滤操作的先后顺序,形成一个过滤链。这样,从前台传递到后台的请求都必须依次通过当前过滤器链上的各个过滤器的过滤功能呢,最后请求才能进入到servlet中进行处理。从这点来说,过滤器链就是职责链模式的一种变形实现。 实现要点
运用效果
适用性
相关模式
总结职责模式的本质是:分离职责,动态组合。分离职责是前提,只有先把复杂的功能分开,拆分成各个小功能,然后才能合理规划和定义职责类;而动态组合才是职责链模式的精华所在,因为要实现请求对象和处理对象的解耦,请求对象并不知道最终的处理对象,所以需要动态地将可能的处理对象组合进来,也正因为组合是动态的,所以可以很方便地修改和增加亲的处理对象,从而使系统具有更好的灵活性和扩展性。另外,由于职责对象只完成一种职责,粒度较小,可以在多个不同功能的职责链中进行复用,增强职责功能的复用性。对职责链模式的介绍就至此为此吧,下一篇将继续讲述另一个行为型模式——命令模式,敬请期待!
参考资料: |
|
来自: 昵称10504424 > 《C#》