设计模式之职责链模式(Chain Of Responsibility)
一、初识职责链模式
一个人在公司工作时间久了,难免遇到一点自己的私事,有私事就可能耽误上班的时间,可能就要请假,那么和谁去请假呢?可能是每个公司都有自己的请假制度。我们不妨假设:请假半天只要和部门主管说一声就行了,请假在半天到2天之间要通过人事部门,而请假超过两天就不那么好申请了,这时可能要总经理或者更高级别的人同意才行了。如果不考虑设计模式直接写代码,要完成这个逻辑就可能用到if—else或者多个if了:
1 class Manager 2 { 3 private: 4 string name; 5 string level; 6 public: 7 //处理请假 8 void deal_holiday(int day) 9 { 10 if(day <= 0.5 && level == "主管") 11 { 12 cout<<level<<" 同意请假 !!"<<endl; 13 return; 14 } 15 if(day > 0.5 && day <= 2 && level == "人事部门") 16 { 17 cout<<level<<" 同意请假 "<<endl; 18 return; 19 } 20 if(day > 2 && level == "总经理") 21 { 22 cout<<level<<" 同意请假 "<<endl; 23 return; 24 } 25 cout<<"各个部门均不同意请假!!!"<<endl; 26 } 27 };
看到这个代码,就知道了存在许多的问题,比如现在要扩展怎么办呢??比如又有了董事长,可以处理一个月的请假,这时就要修改这么Manager类中的deal_holiday方法了,显然不符合单一职责原则(SRP)和开放-封闭原则(OCP),这种类自然也是不合理的类了。现在可以说一说职责链模式了,实际上这种情形是适合职责链模式的:现在有一个请假信息需要处理,首先是主管处理;主管处理不了,交给人事部门处理;人事部门处理不了,交给总经理处理(如果总经理是最后可能处理请假信息的管理者,那么总经理一定会给出一个答复)。这就构成了一个职责链:
如果我们把每个管理者都写一个类,每个管理者都负责处理请假信息,但是只有当主管处理不了时,才交给人事部门处理,以此类推。那么就是可以写出来一个职责链模式的代码:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 //管理者类 7 class Manager 8 { 9 private: 10 string name; 11 Manager *nextManager; 12 public: 13 Manager(string n,Manager *m = NULL) : name(n),nextManager(m){}; 14 void set_name(string name) 15 { 16 this->name = name; 17 } 18 string get_name() 19 { 20 return this->name; 21 } 22 //设置下一个要处理请假信息的管理者 23 void set_next_manager(Manager *m) 24 { 25 this->nextManager = m; 26 } 27 Manager* get_next_manager() 28 { 29 return nextManager; 30 } 31 //处理请假 32 virtual void deal_holiday(int day){}; 33 }; 34 //主管类 35 class ZhuGuan : public Manager 36 { 37 public: 38 ZhuGuan(string n,Manager *m = NULL):Manager(n,m){}; 39 virtual void deal_holiday(int day) 40 { 41 if(day <= 0.5) 42 { 43 cout<<"主管:"<<get_name()<<"同意请假!!"<<endl; 44 } 45 else if(get_next_manager() != NULL) 46 { 47 get_next_manager()->deal_holiday(day); 48 } 49 } 50 }; 51 //人事部门类 52 class RenShi : public Manager 53 { 54 public: 55 RenShi(string n,Manager *m = NULL):Manager(n,m){}; 56 virtual void deal_holiday(int day) 57 { 58 if(day < 2) 59 { 60 cout<<"人事部:"<<get_name()<<"同意请假!!"<<endl; 61 } 62 else if(get_next_manager() != NULL) 63 { 64 get_next_manager()->deal_holiday(day); 65 } 66 } 67 }; 68 class ZongJingLi : public Manager 69 { 70 public: 71 ZongJingLi(string n,Manager *m = NULL):Manager(n,m){}; 72 virtual void deal_holiday(int day) 73 { 74 if(day < 30) 75 { 76 cout<<"总经理:"<<get_name()<<"同意请假!!"<<endl; 77 } 78 //总经理室最后一个进行处理的,所以一定要给出一个答复 79 else 80 { 81 cout<<"总经理:"<<get_name()<<"关于你说的请假的事情,由于时间太长,以后再说吧!!"<<endl; 82 } 83 } 84 }; 85 int main() 86 { 87 //主管Mike 88 ZhuGuan Z_G("Mike"); 89 //人事部Tom 90 RenShi R_S("Tom"); 91 //总经理Vincent 92 ZongJingLi Z_J_L("Vincent"); 93 94 Z_G.set_next_manager(&R_S); 95 R_S.set_next_manager(&Z_J_L); 96 97 Z_G.deal_holiday(0.5); 98 Z_G.deal_holiday(1); 99 Z_G.deal_holiday(3); 100 Z_G.deal_holiday(31); 101 return 0; 102 }
看上面的代码,我们不难发现,要完成系统的扩展时很方便,只需要在增加管理者类,并且这个"职责链”是在客户端动态确定的哦。当然了如果您现在已经是部门经理了,那么可能您可以直接向总经理请假,这也是可以的。
二、再看职责链模式
首先看看GoF怎么说:
职责链模式(Chain Of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
说的通俗一点就是:职责链模式就是把让请求沿着链条一直传下去,知道有一个对象能够处理这个请求。在职责链上的对象都有机会处理到请求,但是只有前一个职责链不能完成请求的处理,才将请求传递给下一个对象。可以看出职责链上的对象到底谁能完成请求的处理是不确定的。并且需要注意的是,职责链是在客户端动态确定的哦,具体职责链上对象的顺序也是动态确定的。还是看看结构图:
三、职责链模式和状态模式
我的初中生活大概是这样的:上午七点起床,八点之前洗脸刷牙吃早饭,然后去上课到十一点四十,中午吃饭,然后午睡,下午一点半继续上课,五点十分下课,然后吃晚饭,从六点二十到九点是晚自习时间,九点之后放学回家,洗洗睡了。
初中的时候请假是这样的流程:首先是可以找班长请假,班长批准请半天,超过半天班长向老师申请,如果请假时间超过一周,老师要跟副年级主任请示,如果请假超出一个月,主任要跟年级正主任请示,然后被批准,或不被批准。
实际上第一种情况就是适合状态模式的,而第二种情况适合职责链模式。为什么呢??状态模式中每个对象之间的变换是在程序编译的时候确定的,也就是编译的时候每个对象就已经知道自己的下一个状态了,这适合每个状态的后继状态基本是比较稳定不会发生变化的情况,比如,初中生活就是那样,每天都是,而职责链模式呢?链上的每个对象是在客户端动态确定的,可能每次都不一样,为什么请假就适合职责链模式呢??就是因为职责是动态确定的,如果某天班主任家里有点事,没来,按照职责链模式可以把班长的在职责链上的下一对象设置为副年级主任。这样其他学生还可以正常请假。而如果按照状态模式的话,如果一旦班主任没来,学生就不能请超过半天的假啦。。。这不太好吧~~~个人感觉状态模式和职责链模式的区别就是这样的。
四、使用职责链模式的场合和优缺点
使用职责链模式的场合:
个人觉得,职责链模式主要用于当一个请求有多种处理方式的时候,并且具体处理方式不确定的情况。
使用职责链模式的优点:
1.增强了系统的可扩展性。
2.使用职责链模式可以避免众多的if或者if-else语句。
3.使用职责链模式可以减小对象之间的耦合性。使得对象之间的联系减小。
4.可以根据需要自由组合工作流程。如工作流程发生变化,可以通过重新分配对象链便可适应新的工作流程。
5.责任的分担。每个类只需要处理自己该处理的工作(不该处理的传递给下一个对象完成),明确各类的责任范围,符合类的最小封装原则。
使用职责链模式的缺点:
1.使用职责链模式的时候会有多个处理者对象,但是实际使用的处理者对象却只有一个,这在某种程度讲是资源的浪费。
2.同时职责链的建立的合理性要靠客户端来保证,增加了程序的复杂性,也有可能由于职责链导致出错。