无废话C#设计模式之十七: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();

        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.

        }

        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));

        }

    }

    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;

        }

    }

    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);

        }

    }

    class NormalGM : CustomerService

    {

        public NormalGM(string name) : base(name) { }

        public override void HandleCase(Case gameCase)

        {

            if (responsibleCaseCategory.Contains(gameCase.CaseCategory))

            {

                gameCase.Reply = "OK";

                Console.WriteLine("The case has been replied by " + name);

                if (gameCase.ImportantCase)

                {

                    Console.WriteLine("The reply should be checked.");

                    leader.HandleCase(gameCase);

                }

                else

                    Console.WriteLine("The reply has been sent to player.");

            }

            else if (leader != null)

            {

                Console.WriteLine(string.Format("{0} reports this case to {1}.", name, leader.Name));

                leader.HandleCase(gameCase);

            }

        }

    }

    class GMManager : CustomerService

    {

        public GMManager(string name) : base(name) { }

        public override void HandleCase(Case gameCase)

        {

            if (responsibleCaseCategory.Contains(gameCase.CaseCategory))

            {

                gameCase.Reply = "OK";

                Console.WriteLine("The case has been replied by " + name);

                if (gameCase.ImportantCase)

                {

                    Console.WriteLine("The reply should be checked.");

                    leader.HandleCase(gameCase);

                }

                else

                    Console.WriteLine("The reply has been sent to player.");

            }

            else if (gameCase.Reply != null)

            {

                Console.WriteLine("The case has been checked by " + name);

                Console.WriteLine("The reply has been sent to player.");

            }

            else if (leader != null)

            {

                Console.WriteLine(string.Format("{0} reports this case to {1}.", name, leader.Name));

                leader.HandleCase(gameCase);

            }

        }

    }

    class GMDirector : CustomerService

    {

        public GMDirector(string name) : base(name) { }

        public override void HandleCase(Case gameCase)

        {

            if (gameCase.Reply != null)

            {

                Console.WriteLine("The case has been checked by " + name);

                Console.WriteLine("The reply has been sent to player.");

            }

            else

            {

                gameCase.Reply = "OK";

                Console.WriteLine("The case has been replied by " + name);

                Console.WriteLine("The reply has been sent to player.");

            }

        }

    }

}

代码执行结果如下图(后面一图为注释掉InitGM()方法中初始化managerTeam代码的执行结果):

 


代码说明

l         Case类代表了问题。CaseCategory属性代表了问题的分类,普通客服和客服经理处理不同分类的问题。ImportantCase属性代表了问题是否是重要问题,如果是重要问题,则需要上级领导审核。Reply属性代表了客服对问题的回复。

l         CustomerService类是责任链模式中的抽象处理者。我们看到,它定义了下个责任人的引用,并且提供了设置这个责任人的方法。当然,它也定义了统一的处理接口。

l         NormalGM是具体处理者,它实现了处理接口。在这里,我们看到普通客服的处理逻辑是,如果这个问题的分类在它负责的分类之外则直接提交给上级领导进行处理(把对象通过责任链传递),否则就回复问题,回复问题之后看这个问题是否是重要问题,如果是重要问题则给上级领导进行审核,否则问题处理结束。

l         GMManager也是一个具体处理者。客服经理处理问题的逻辑是,首先判断问题的问题是否在它负责的分类之内,如果是的话则进行处理(重要问题同样提交给上级处理),如果不是的话就看问题是否有了回复,如果有回复说明是要求审核的问题,审核后问题回复,如果问题还没有回复则提交给上级处理。

l         GMDirector的处理流程就相对简单了,它并没有上级了,因此所有问题必须在它这里结束。对于没有回复的问题则进行回复,对于要求审核的问题则进行审核。

l         再来看看客户端的调用。首先,执行了InitGM()方法来初始化客服团队的数据,在这里我们的团队中有9个普通客服、2个客服经理和1个客服总监。普通客服只能回复分类1和分类2的问题,而客服经理只能回复分类3和分类4的问题。

l         然后,调用InitCOR()方法来初始化责任链,在这里我们并没有简单得设置普通客服的上级是客服经理、客服经理的上级是客服总监,而是自动根据是否有客服经理这个角色来动态调整责任链,也就是说如果没有客服经理的话,普通客服直接向客服总监汇报。

l         最后,我们模拟了一些问题数据进行处理。对于客户端(玩家)来说任何普通客服角色都是一样的,因此我们为所有问题随机分配了普通客服作为责任链的入口点。

l         首先来分析有客服经理时的处理情况。分类为1的问题直接由普通客服处理完毕。分类为2的重要问题由普通客服回复后再由客服经理进行审核。分类为3的问题直接由普通客服提交到客服经理进行处理。分类为4的重要问题也直接由普通客服提交到客服经理进行处理,客服经理回复后再提交到客服总监进行审核。分类为5的问题由普通客服提交到客服经理进行处理,客服经理再提交给客服总监进行处理。

l         再来分析没有客服经理时的处理情况。分类为1的问题直接由普通客服处理完毕。分类为2的重要问题由普通客服回复后再由客服总监进行审核。分类为3的问题直接由普通客服提交到客服总监进行处理。分类为4的重要问题也直接由普通客服提交到客服总监进行处理。分类为5的问题也直接由普通客服提交到客服总监进行处理。

l         如果没有责任链模式,这个处理流程将会多么混乱。

何时采用

l         从代码角度来说,如果一个逻辑的处理由不同责任对象完成,客户端希望能自定义这个处理流程并且不希望直接和多个责任对象发生耦合的时候可以考虑责任链模式。

l         从应用角度来说,如果对一个事情的处理存在一个流程,需要经历不同的责任点进行处理,并且这个流程比较复杂或只希望对外公开一个流程的入口点的话可以考虑责任链模式。其实,责任链模式还可以在构架的层次进行应用,比如.NET中的层次异常处理关系就可以看作是一种责任链模式。

实现要点

l         有一个抽象责任角色,避免各责任类型之间发生耦合。

l         抽象责任角色中定义了后继责任角色,并对外提供一个方法供客户端配置。

l         各具体责任类型根据待处理对象的状态结合自己的责任范围来判断是否能处理对象,如果不能处理提交给上级责任人处理(也就是纯的责任模式,要么自己处理要么提交给别人)。当然,也可以在进行部分处理后再提交给上级处理(也就是不纯的责任链模式)。

l         需要在客户端链接各个责任对象,如果链接的不恰当,可能会导致部分对象不能被任何一个责任对象进行处理。

注意事项

l         责任链模式和状态模式的区别在于,前者注重责任的传递,并且责任链由客户端进行配置,后者注重对象状态的转换,这个转换过程对客户端是透明的。

转载于:https://www.cnblogs.com/lovecherry/archive/2007/10/14/924041.html

(原创)无废话C#设计模式之十七:Chain Of Resp.相关推荐

  1. (原创)无废话C#设计模式之二十二:总结(针对GOF23)

    无废话C#设计模式之二十二:总结(针对GOF23) 比较 设计模式 常用程度 适用层次 引入时机 结构复杂度 Abstract Factory 比较常用 应用级 设计时 比较复杂 Builder 一般 ...

  2. (原创)无废话C#设计模式之十二:Bridge

    无废话C#设计模式之十二:Bridge 意图 将抽象部分与实现部分分离,使它们都可以独立的变化. 场景 还是说我们要做的网络游戏,多个场景需要扩充的问题我们已经采用了创建型模式来解决.现在的问题就是, ...

  3. (原创)无废话C#设计模式之十一:Composite

    无废话C#设计模式之十一:Composite 意图 将对象组合成树形结构以表示"部分-整体"的层次结构.Composite模式使得用户对单个对象和组合对象的使用具有一致性. 场景 ...

  4. (原创)无废话C#设计模式之十九:Observer

    无废话C#设计模式之十九:Observer 意图 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新. 场景 这次不说游戏了,假设我们需要在一个W ...

  5. (原创)无废话C#设计模式之四:Factory Method

    无废话C#设计模式之四:Factory Method       <a href='http://xgb.xgb.cc'>a</a>   <a href='http:// ...

  6. 无废话ExtJs 入门教程十七[列表:GridPanel]

    无废话ExtJs 入门教程十七[列表:GridPanel] extjs技术交流,欢迎加群(521711109) 在Extjs中,GridPanel用于数据显示,即我们平时说的列表页.在本节中,我们先对 ...

  7. 无废话-SQL Server 2005新功能(1) - TSQL

    无废话-SQL Server 2005新功能(1) - TSQL SQL Server 2005相对于SQL Server 2000改进很大,有些还是非常实用的. 举几个例子来简单说明 这些例子我引用 ...

  8. C++报错无效的预处理命令include_无废话--Mac OS, VS Code 搭建c/c++基本开发环境

    无废话,直接上步骤. 1) 安装 xcode. 打开App Store,搜索xcode,进行下载安装. 2)执行命令: xcode-select --install 安装命令行工具. 3)安装VS C ...

  9. 【浙大网新图灵通讯】无废话简单高效C#编码规范20100611

    无废话简单高效C#编码规范20100611   自己看自己的代码啥问题也没有,但是一个团队.N多个人.N多年的代码,就比较难维护.难阅读了,什么样的不好的习惯.错误漏洞能出来的就全出来了. 第1章   ...

最新文章

  1. 12,缓冲运动。匀速运动停止条件
  2. 最大权闭合 图 讲解
  3. kuka机器人股票代码_【内幕】溢价收购KUKA机器人 美的钱从哪来?
  4. mysql的代码大全_MySql数目字函数大全
  5. json对象转换成string的方法
  6. 腾讯开源软件镜像站上线
  7. P4445 最长回文串
  8. 实现用户裂变的5大原则
  9. 2017-2018-20172309 《程序设计与数据结构》第五周学习总结
  10. js无限分级 树_js实现无限级树形导航列表效果代码
  11. qt的qopengl绘制箱子,实现翻转
  12. matlab的textscan与textread区别(转)
  13. 树莓派python编程自学-树莓派Python编程指南 中文PDF扫描版
  14. 无限级分销管理系统的设计与实现
  15. filtering_audio.c/filtering_video.c 解读
  16. 腾讯视频投屏显示无法连接服务器,腾讯视频突然不能投屏怎么办 投屏连接失败解决方法...
  17. p0f - 被动探测操作系统工具
  18. 顶流AI大赛背后:OPPO小布助手的技术势能和促成的想象力
  19. Tensorflow使用object_detetcion安装教程
  20. win10下定时任务备份,bat脚本运行

热门文章

  1. 如何在Linux中使用ulimit命令
  2. Android Studio教程– Hello World App
  3. WinForm XML
  4. C#一个完整的电子邮件操作类
  5. React Native项目使用react-apollo实现更新缓存的两种方式
  6. 排序总结---常用的排序算法总结,java和js实现
  7. Yii2数据库操作的各种写法
  8. 各版本Sql Server下载地址全
  9. Glade3 tutorial in chinese
  10. 返璞归真-SOC起源