现在的位置: 首页 > 综合 > 正文

设计模式 – 职责链模式

2013年12月12日 ⁄ 综合 ⁄ 共 3364字 ⁄ 字号 评论关闭

从本文开始我们来介绍行为模式。根据GOF的说法,设计模式可以分为:创建型模式,结构型模式和行为型模式。

行为型模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述它们之间的通信。

我们先来介绍第一种行为型模式:职责链。

 

意图

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

 

结构图

职责链模式的关键在于“链”,从上面的结构图也可以看到,基类接口有个重要的特性,就是自己维护了一个链。

根据GOF的说法,有多种实现链的方式:

1. 在基类接口中定义链接;

2. 子类中定义链接;

3. 使用已有的链接。

本文使用第一种方式,就是基类接口中定义链接。

 

举个例子:一个公司里面,一个员工申请用公司的钱买书,或者买其他东西,那么就需要经过领导的审批。组长可以审批,部分经理可以,总经理可以审批。比如组长最多可以审批1000的预算,部门经理可以审批5000的预算,总经理就可以审批任何数目的预算了。

在这个例子里面,组长,部门经理,总经理都有一个共同的能力:审批。

对于普通员工来讲,他只需要提交这个申请给他的直接领导,然后就等结果。我们可以把组长,部门经理,总经理链接起来,就是一个职责链。

员工的这个申请会在这个链中传递,知道有一个对象来处理这个请求,而员工并不需要关心具体哪个对象来处理,他只要等结果就行了。

职责链的关键就在于这条链怎么建立起来。

比如,我们建立一条链,这条链里面只有组长和部门经理,那么这里就有个问题,超过5000的预算得不到审批,也就是没有对象可以处理超过5000的预算请求了。这也是职责链模式的一个问题:一个请求不一定会被处理(链中没有一个合适的对象可以处理这个请求)。

我们用代码来实现一下上面的例子:

class CLeader
{
public:
	CLeader(CLeader* leader = 0, float budget = 0):_successor(leader), _budget(budget){}

	virtual void ApproveBudget(float budget) = 0;

protected:
	CLeader* _successor;
	float _budget;
};

class CTeamLeader: public CLeader
{
public:
	CTeamLeader(CLeader* leader = 0, float budget = 0):CLeader(leader, budget){}

	virtual void ApproveBudget(float budget)
	{
		if (budget <= _budget)
		{
			std::cout << "TeamLeader approve process\n";
		}
		else
		{
			if (_successor)
			{
				_successor->ApproveBudget(budget);
			}
		}
	}

};

class CDM: public CLeader
{
public:
	CDM(CLeader* leader = 0, float budget = 0): CLeader(leader, budget){}

	virtual void ApproveBudget(float budget)
	{
		if (budget <= _budget)
		{
			std::cout << "DM approve process\n";
		}
		else
		{
			if (_successor)
			{
				_successor->ApproveBudget(budget);
			}
		}
	}

};

class CGM: public CLeader
{
public:
	CGM(CLeader* leader = 0, float budget = 0): CLeader(leader, budget){}

	virtual void ApproveBudget(float budget)
	{
		std::cout << "GM approve process\n";
	}

};

CLeader是一个抽象接口,它定义了审批接口:ApproveBudget。同时里面还有2个属性,一个是指向后继者的指针_successor,另外一个是审批上限。

CTeamLeader是组长,CDM(DM: Department Manager)是部门经理, CGM是总经理。在这些类的构造函数里面可以设置每个领导的后继者(也就是当某位领导无法审批的时候,需要提交给上一级审批)和审批上限。

抽象接口CLeader里面的后继者是职责链模式的一个关键。用这个后继者来实现请求在链中传递。

看一下类图,可以增加对这个模式的理解:

 可以明显的看到CLeader里面有个successor指引。

看看客户端怎么调用:

	CLeader* gm = new CGM();
	CLeader* dm = new CDM(gm, 5000);
	CLeader* teamleader = new CTeamLeader(dm, 1000);

	//teamleader可以审批
	teamleader->ApproveBudget(500);

	//teamleader不可以审批2000的预算,那么就提交的teamleader的后继者部门经理。部门经理可以审批2000的预算
	teamleader->ApproveBudget(2000);

	//对于8000的预算,部门经理也不可以审批,那就交给gm(总经理)
	teamleader->ApproveBudget(8000);


	delete gm;
	delete dm;
	delete teamleader;

这里,我们创建了3个对象,组长,部门经理和总经理。设置部分经理为组长的后继者,部门经理的后继这是总经理,总经理就没有后继者了,也就是说总经理是这个链的末端。同时设置组长的审批上限是1000块,部门经理是5000块,总经理没有限制。对于员工来讲,500预算申请,2000预算申请和8000预算申请,都是提交给主管他的组长。然后等结果,至于谁来审批,员工无需关心。也就是意味着请求的发送者和接收者解耦了,或者说是松耦合吧。
这就是职责链模式的基本概况。

 

优点:

1. 降低请求的发送者和接收者的耦合度,发送者只需向一条链发送请求,然后链中的哪个对象来处理,发送者无需关心;

2. 增强了给对象指派职责的灵活性,可以在运行时动态修改链。

 

缺点:

1. 构造这条链是一个负担;

2. 因为请求没有一个明确的接收者,不能保证请求一定会被这条链正确的处理,有时可能到达链的末端都得不到处理。

 

再来考虑例子里面的一个问题,我们是否可以让员工将预算请求递交给链的第二或者其他的对象(不是第一个对象)。比如:

//当然也可以让部门经理来审批500的预算,这个就跳过了teamleader,不给teamleader面子啊,呵呵。
	//从职责链的角度来讲,链中的任何一个对象都可以处理请求,当然,我们在实现链中的类的时候可以使用一些处理规则,
	//比如这里的预算上限,然后根据规则来选择一个合适的handler。
	dm->ApproveBudget(500);

	//gm就可以审批任何数目的预算了。
	gm->ApproveBudget(500);

从模式的角度讲,并不会限制这些。这些都看具体的应用,如果真的有这种需求,当然可以了。比方说把请求提交给链的第二个对象,没什么不可以的。这个就是一些特殊处理。

当我们创建了一条链之后,通常都是把请求提交给链的第一个对象,如果有特殊需要,也可以提交给链中的其他对象。

这个例子的后继者是放在抽象接口中的,其实后继者也有其他的实现方式,这里就不再用代码来模拟了。

 

相关模式

职责链常和Composite一起使用。这种情况下,一个构件的父构件可以作为它的后继者(也就是无需再额外定义后继者了)。

比如,我们用Composite模式来构造公司里面的员工。普通员工,组长,部门经理,总经理等等都是公司的员工。那么我们就可以用Composite来抽象这些成员,比如普通员工是一个Leaf,然后组长下面有一系列的普通员工,部门经理下面又有一系列的组长,总经理下面就有一系列的部门经理。那么普通员工的父构件组长就是他的后继者,组长的父构件部门经理就是组长的后继者。也就是说Composite模式里面本身就已经有后继者的关系了,在这种系统里面如果要使用职责链模式的话,就无需再额外定义后继者了。

 

抱歉!评论已关闭.