职责链模式

思考并回答以下问题:

  • 职责链可以解决if,elseif问题,怎么理解?

案例介绍

加薪向经理申请,经理没权利,然后向总监上报,总监也没权限,向总经理上报。

加薪代码初步

无论加薪还是请假,都是一种申请。申请就应该有申请类别、申请内容和申请数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 申请
class Request
{
// 申请类别
private string requestType;

public string RequestType
{
get { return requestType; }
set { requestType = value; }
}

// 申请内容
private string requestContent;

public string RequestContent
{
get { return requestContent; }
set { requestContent = value; }
}

// 申请数量
private int number;

public int Number
{
get { return number; }
set { number = value; }
}
}

经理、总监、总经理都是管理者,他们在对‘申请’处理时,需要做出判断,是否有权决策。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Manager
{

protected string name;

public Manager(string name)
{
this.name = name;
}

// 得到结果
public void GetResult(ManagerLevel managerLevel, Request request)
{
if (managerLevel == ManagerLevel.经理)
{
if (request.RequestType == "请假" && request.Number <= 2)
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else
{
Console.WriteLine("{0}:{1} 数量{2} 我无权处理", name, request.RequestContent, request.Number);
}

}
else if (managerLevel == ManagerLevel.总监)
{
if (request.RequestType == "请假" && request.Number <= 5)
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else
{
Console.WriteLine("{0}:{1} 数量{2} 我无权处理", name, request.RequestContent, request.Number);
}
}
else if (managerLevel == ManagerLevel.总经理)
{
if (request.RequestType == "请假")
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else if (request.RequestType == "加薪" && request.Number <= 500)
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else if (request.RequestType == "加薪" && request.Number > 500)
{
Console.WriteLine("{0}:{1} 数量{2} 再说吧", name, request.RequestContent, request.Number);
}
}

}
}

客户端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
enum ManagerLevel { 
"经理",
"总监",
"总经理"
}

class Program
{
static void Main(string[] args)
{
Manager jinli = new Manager("金利");
Manager zongjian = new Manager("宗剑");
Manager zhongjingli = new Manager("钟精励");

Request request = new Request();
request.RequestType = "加薪";
request.RequestContent = "小菜请求加薪";
request.Number = 1000;

jinli.GetResult(ManagerLevel.经理, request);
zongjian.GetResult(ManagerLevel.总监, request);
zhongjingli.GetResult(ManagerLevel.总经理, request);

Request request2 = new Request();
request2.RequestType = "请假";
request2.RequestContent = "小菜请假";
request2.Number = 3;

jinli.GetResult(ManagerLevel.经理, request2);
zongjian.GetResult(ManagerLevel.总监, request2);
zhongjingli.GetResult(ManagerLevel.总经理, request2);

Console.Read();
}
}

‘管理者’类里面的‘结果’方法比较长,加上有太多的分支判断,这其实是非常不好的设计。很难讲当中还会不会增加其他的管理类别,比如项目经理、部门经理、人力总监、副总经理等等。那就意味着都需要去更改这个类,这个类承担了太多的责任,这违背了单一职责原则,增加新的管理类别,需要修改这个类,违背了开放-封闭原则。

应该如何下手去重构它呢?

可能会增加管理类别,那就意味着这里容易变化,可以把这些公司管理者的类别各做成管理者的子类,这就可以利用多态性来化解分支带来的僵化。

那如何解决经理无权上报总监,总监无权再上报总经理这样的功能呢?

让它们之间有一定的关联,把用户的请求传递,直到可以解决这个请求为止。

职责链模式

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

这里发出这个请求的客户端并不知道这当中的哪一个对象最终处理这个请求,这样系统的更改可以在不影响客户端的情况下动态地重新组织和分配责任。

职责链模式(Chain of Responsibility)结构图

  • Handler类,定义一个处理请示的接口。
  • ConcreteHandler类,具体处理者类,处理它所负责的请求,可访问它的后继者,如果可处理该请求,就处理之,否则就将该请求转发给它的后继者。
  • ConcreteHandler1,当请求数在0到10之间则有权处理,否则转到下一位。
  • ConcreteHandler2,当请求数在10到20之间则有权处理,否则转到下一位。

客户端代码,向链上的具体处理者对象提交请求。

1
2
3
4
5
6
7
8
9
10
11
abstract class Handler
{
protected Handler successor;

public void SetSuccessor(Handler successor)
{
this.successor = successor;
}

public abstract void HandleRequest(int request);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ConcreteHandler1 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 0 && request < 10)
{
Console.WriteLine("{0} 处理请求 {1}",
this.GetType().Name, request);
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ConcreteHandler2 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 10 && request < 20)
{
Console.WriteLine("{0} 处理请求 {1}",
this.GetType().Name, request);
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ConcreteHandler3 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 20 && request < 30)
{
Console.WriteLine("{0} 处理请求 {1}",
this.GetType().Name, request);
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Program
{
static void Main(string[] args)
{
Handler h1 = new ConcreteHandler1();
Handler h2 = new ConcreteHandler2();
Handler h3 = new ConcreteHandler3();
h1.SetSuccessor(h2);
h2.SetSuccessor(h3);

int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };

foreach (int request in requests)
{
h1.HandleRequest(request);
}

Console.Read();

}
}

职责链的好处

这当中最关键的是当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它。这样做的好处是请求者不用管哪个对象来处理,反正该请求会被处理就对了。这就使得接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。这也就大大降低了耦合度。

由于是在客户端来定义链的结构,也就是说,可以随时地增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性。不过也要当心,一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理,这就很糟糕了。需要事先考虑全面。

这就跟现实中邮寄一封信,因地址不对,最终无法送达一样。

就刚才的例子而言,最重要的有两点,一个是需要事先给每个具体管理者设置他的上司是哪个类,也就是设置后继者。另一点是需要在每个具体管理者处理请求时,做出判断,是可以处理这个请求,还是必须要‘推卸责任’,转移给后继者去处理。

其实就是把现在写的这个管理者类当中的那些分支,分解到每一个具体的管理者类当中,然后利用事先设置的后继者来实现请求处理的权限问题。”

加薪代码重构

先来改造这个管理者类,此时它将成为抽象的父类了,其实它就是Handler。

代码结构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 管理者
abstract class Manager
{
protected string name;
//管理者的上级
protected Manager superior;

public Manager(string name)
{
this.name = name;
}

//设置管理者的上级
public void SetSuperior(Manager superior)
{
this.superior = superior;
}

//申请请求
abstract public void RequestApplications(Request request);
}

经理类就可以去继承这个‘管理者’类,只需重写‘申请请求’的方法就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 经理
class CommonManager : Manager
{
public CommonManager(string name)
: base(name)
{ }
public override void RequestApplications(Request request)
{

if (request.RequestType == "请假" && request.Number <= 2)
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else
{
if (superior != null)
superior.RequestApplications(request);
}

}
}

‘总监’类同样继承‘管理者类’。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 总监
class Majordomo : Manager
{
public Majordomo(string name)
: base(name)
{ }
public override void RequestApplications(Request request)
{

if (request.RequestType == "请假" && request.Number <= 5)
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else
{
if (superior != null)
superior.RequestApplications(request);
}

}
}

‘总经理’的权限就是全部都需要处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 总经理
class GeneralManager : Manager
{
public GeneralManager(string name)
: base(name)
{ }
public override void RequestApplications(Request request)
{

if (request.RequestType == "请假")
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else if (request.RequestType == "加薪" && request.Number <= 500)
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else if (request.RequestType == "加薪" && request.Number > 500)
{
Console.WriteLine("{0}:{1} 数量{2} 再说吧", name, request.RequestContent, request.Number);
}
}
}

由于我们把你原来的一个‘管理者’类改成了一个抽象类和三个具体类,此时类之间的灵活性就大大增加了,如果我们需要扩展新的管理者类别,只需要增加子类就可以。

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Program
{
static void Main(string[] args)
{

CommonManager jinli = new CommonManager("金利");
Majordomo zongjian = new Majordomo("宗剑");
GeneralManager zhongjingli = new GeneralManager("钟精励");
jinli.SetSuperior(zongjian);
zongjian.SetSuperior(zhongjingli);

Request request = new Request();
request.RequestType = "请假";
request.RequestContent = "小菜请假";
request.Number = 1;
jinli.RequestApplications(request);

Request request2 = new Request();
request2.RequestType = "请假";
request2.RequestContent = "小菜请假";
request2.Number = 4;
jinli.RequestApplications(request2);

Request request3 = new Request();
request3.RequestType = "加薪";
request3.RequestContent = "小菜请求加薪";
request3.Number = 500;
jinli.RequestApplications(request3);

Request request4 = new Request();
request4.RequestType = "加薪";
request4.RequestContent = "小菜请求加薪";
request4.Number = 1000;
jinli.RequestApplications(request4);

Console.Read();

}
}

结果显示

1
2
3
4
金利:小菜请假 数量1被批准
宗剑:小菜请假 数量4被批准
钟精励:小菜请求加薪 数量500 被批准
钟精励:小菜请求加薪 数量1000 再说吧

很好地解决了原来大量的分支判断造成难维护、灵活性差的问题。

加薪成功

可在客户端改变职责链的结构,跳过总监直接找总经理处理这事,这也体现了职责链灵活性的一个方面。

0%