设计模式系列文章之Chain Of Resp(责任链模式)
意图
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象能处理请求为止。
场景
假设我们在制作一个游戏的客服系统,客服有三种角色,分别是普通客服、客服经理和客服总监。玩家在网站中提问后,根据问题的分类和重要性处理的流程不一样。规则如下:
l 分类为1(游戏问题)和2(角色问题)的问题会由普通客服处理。
l 分类为3(充值问题)和4(账户问题)的问题会由客服经理处理。
l 普通客服和客服经理都没有能处理的问题由客服总监进行处理。
l 所有的问题都分为普通问题和重要问题,如果问题是重要问题则需要上一级的客服进行审核(普通客服回复的问题需要客服经理审核、客服经理回答的问题需要客服总监审核)。
这个处理的业务规则比较复杂,您可能会想到创建三个具体客服类型继承抽象客服,然后根据问题的性质传给不同的具体客服来处理。可是这样做有几个缺点:
l 客户端需要知道三个角色能处理的问题属性,三个角色能处理的问题应该只有三个角色自己清楚就可以了。
l 客户端需要和三个角色发生耦合,并且在一个处理过程中会先后调用不同的角色。普通客服在回复了重要问题后应该通知客服经理来审核,现在这个过程交给了客户端去做
客户端知道了太多客服处理问题的细节,对于玩家来说他只需要知道把这些问题告诉客服人员并且得到客服人员的处理结果,具体背后的处理流程是怎么样的它不用知道。由此,引入责任链模式来解决这些问题。
示例代码
using System;
using System.Collections.Generic;
using System.Text;
namespace ChainOfRespExample
{
class Program
{
static List<CustomerService> gmTeam = new List<CustomerService>();
static List<CustomerService> managerTeam = new List<CustomerService>();
static List<CustomerService> directorTeam = new List<CustomerService>();
static Random random = new Random();
l 然后,调用InitCOR()方法来初始化责任链,在这里我们并没有简单得设置普通客服的上级是客服经理、客服经理的上级是客服总监,而是自动根据是否有客服经理这个角色来动态调整责任链,也就是说如果没有客服经理的话,普通客服直接向客服总监汇报。
static void InitCOR()
{
if (managerTeam.Count > 0)
{
foreach (CustomerService cs in gmTeam)
cs.SetLeader(managerTeam[random.Next(managerTeam.Count)]);
}
else
{
foreach (CustomerService cs in gmTeam)
cs.SetLeader(directorTeam[random.Next(directorTeam.Count)]);
}
foreach (CustomerService cs in managerTeam)
cs.SetLeader(directorTeam[random.Next(directorTeam.Count)]);
// These configs above depends on business logic.
}
l 再来看看客户端的调用。首先,执行了InitGM()方法来初始化客服团队的数据,在这里我们的团队中有9个普通客服、2个客服经理和1个客服总监。普通客服只能回复分类1和分类2的问题,而客服经理只能回复分类3和分类4的问题。
static void InitGM()
{
for (int i = 1; i <= 9; i++)
{
CustomerService gm = new NormalGM("gm" + i);
gm.SetResponsibleCaseCategory(new int[] { 1, 2 });
gmTeam.Add(gm);
}
for (int i = 1; i <= 2; i++)
{
CustomerService manager = new GMManager("manager" + i);
manager.SetResponsibleCaseCategory(new int[] { 3, 4 });
managerTeam.Add(manager);
}
for (int i = 1; i <=1; i++)
directorTeam.Add(new GMDirector("director" + i));
// These configs above should be from database.
}
static void Main(string[] args)
{
InitGM();
InitCOR();
CustomerService gm = gmTeam[random.Next(gmTeam.Count)];
gm.HandleCase(new Case(1, false));
Console.WriteLine(Environment.NewLine);
gm = gmTeam[random.Next(gmTeam.Count)];
gm.HandleCase(new Case(2, true));
Console.WriteLine(Environment.NewLine);
gm = gmTeam[random.Next(gmTeam.Count)];
gm.HandleCase(new Case(3, false));
Console.WriteLine(Environment.NewLine);
gm = gmTeam[random.Next(gmTeam.Count)];
gm.HandleCase(new Case(4, true));
Console.WriteLine(Environment.NewLine);
gm = gmTeam[random.Next(gmTeam.Count)];
gm.HandleCase(new Case(5, true));
}
}
l Case类代表了问题。CaseCategory属性代表了问题的分类,普通客服和客服经理处理不同分类的问题。ImportantCase属性代表了问题是否是重要问题,如果是重要问题,则需要上级领导审核。Reply属性代表了客服对问题的回复。
class Case
{
private int caseCategory;
public int CaseCategory
{
get { return caseCategory; }
}
private bool importantCase;
public bool ImportantCase
{
get { return importantCase; }
}
private string reply;
public string Reply
{
get { return reply ; }
set { reply = value; }
}
public Case(int caseCategory, bool importantCase)
{
this.caseCategory = caseCategory;
this.importantCase = importantCase;
}
}
l CustomerService类是责任链模式中的抽象处理者。我们看到,它定义了下个责任人的引用,并且提供了设置这个责任人的方法。当然,它也定义了统一的处理接口。
abstract class CustomerService
{
protected CustomerService leader;
protected List<int> responsibleCaseCategory = new List<int>();
protected string name;
public string Name
{
get { return name; }
}
public CustomerService(string name)
{
this.name = name;
}
public void SetLeader(CustomerService leader)
{
this.leader = leader;
}
public abstract void HandleCase(Case gameCase);
public void SetResponsibleCaseCategory(int[] responsibleCaseCategory)
{
foreach (int i in responsibleCaseCategory)
this.responsibleCaseCategory.Add(i);
}
}
l NormalGM是具体处理者,它实现了处理接口。在这里,我们看到普通客服的处理逻辑是,如果这个问题的分类在它负责的分类之外则直接提交给上级领导进行处理(把对象通过责任链传递),否则就回复问题,回复问题之后看这个问题是否是重要问题,如果是重要问题