美文网首页设计模式 C#程序员架构设计与重构
流程业务,各司其职,『责任链模式』

流程业务,各司其职,『责任链模式』

作者: 圣杰 | 来源:发表于2016-11-17 18:18 被阅读224次

    目录:设计模式之小试牛刀
    源码路径:Github-Design Pattern


    定义:(Chain of Responsibility)

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

    类图:

    责任链模式通用类图

    启示:

    用过ERP软件的都知道,从生产制造到采购销售,库存管理到财务管理,各个模块下各个环节基本上都是按照既定业务流程进行规范作业的。各个环节都有对应的人员负责,承担相应职责。
    拿采购环节举例:
    开发经理向公司反馈需要要给程序猿升级设备以提高工作效率,申请采购一批电脑设备。公司采购部门有采购员小责做了一张采购申请单,保存后,需要直接上级采购经理任经理确认提交审核,若总金额超过经理审核最大额度,需继续提交到公司老总审批,只有CEO链总批准后,这张采购申请单才能生效。
    这个环节中共包含三个主要操作,保存、提交、审核,且分别对应三个角色进行处理。
    在这个例子中,采购员负责做单;采购经理负责检查确认;CEO负责审核。
    采购员具有权限:保存
    采购经理具有权限:保存、提交、审核(只可审核总金额低于5万的订单)
    CEO具有权限:保存、提交、审核
    这不就是一条业务被不同职责分割成三个操作吗?
    来,咱们用『责任链模式』一探究竟。

    代码:

    先来定义单据,单据主要包括单据名称、单据编号、物料、数量、价格、总金额,还有制单员

       /// <summary>
        /// 单据状态枚举
        /// </summary>
        public enum BillStatus
        {
            Open,
            Saved,
            Submitted,
            Audited
        }
    
        public abstract class Bill
        {
            public string BillName { get; set; }
    
            public string BilNo { get; set; }
    
            public BillStatus Status { get; set; }
    
            public string MaterialName { get; set; }
    
            public int Qty { get; set; }
    
            public decimal Price { get; set; }
    
            public decimal Amount
            {
                get
                {
                    return Qty * Price;
                }
            }
    
            /// <summary>
            /// 做单员
            /// </summary>
            public BillHandler BillMaker { get; set; }
        }
    
        public class PurchaseBill : Bill
        {
            public PurchaseBill()
            {
                base.Status = BillStatus.Open;
                base.BillName = "采购申请单";
            }
        }
    

    细心的同学注意到我们单据中定义的制单员BillMaker引入了BillHandler,这个就是我们『责任链模式』的核心所在,Handler角色。主要定义并实现了一个HandleBill方法(模板方法模式)供子类共用;同时定义了一个抽象方法DoBillOperation让子类实现自己独立的业务逻辑;还有一个重要的属性Successor,即下一处理者,是流程流转的必要属性啊,切记切记。
    另外:单据的审批流是基于角色、权限的,所以我在BillHandler类定义了Permissions权限集合,和公共的权限检查方法。
    上代码:

        /// <summary>
        /// 单据处理角色
        /// </summary>
        public abstract class BillHandler
        {
            /// <summary>
            /// 单据处理者姓名
            /// </summary>
            public string UserName { get; set; }
    
            /// <summary>
            /// 单据处理者具有的权限
            /// </summary>
            public List<string> Permissions { get; set; }
    
            public bool CheckPermission(string permission)
            {
                return Permissions.Contains(permission);
            }
    
            //声明下一个处理者
            protected BillHandler Successor { get; set; }
    
    
            /// <summary>
            /// 处理单据
            /// </summary>
            /// <param name="bill"></param>
            public void HandleBill(Bill bill)
            {
                //单据处理从保存开始
                if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
                {
                    this.DoBillOperation(bill);
                }
                else
                {
                    this.Successor.DoBillOperation(bill);
                }
            }
    
            public abstract void DoBillOperation(Bill bill);
    
        }
    

    先来看看采购员的实现:权限是动态的,为了演示方便,我们在代码中写死权限。虽然采购员只有保存权限,但是我们还是给他实现了提交、审核的逻辑。因为权限是动态的嘛。(代码中限定只可审核金额低于5000元的单);且在默认构造函数指定下一流程处理者(经理)

     public class Purchaser : BillHandler
        {
            private List<string> permissions = new List<string>() { "SAVE" };
    
           public Purchaser(string username)
            {
                base.UserName = username;
                base.Permissions = permissions;
                base.Successor = new Manager("经理--任经理");//在构造函数中指定下一个处理者
            }
    
            public override void DoBillOperation(Bill bill)
            {
                if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
                {
                    bill.Status = BillStatus.Saved;
                    Console.WriteLine(string.Format("{0}:{1}已经保存!", this.UserName, bill.BilNo));
                }
                
                if (CheckPermission("SUBMIT") && bill.Status == BillStatus.Saved)
                {
                    bill.Status = BillStatus.Submitted;
                    Console.WriteLine(string.Format("{0}:{1}已经提交!", this.UserName, bill.BilNo));
                }
                
                if (CheckPermission("AUDIT") && bill.Status == BillStatus.Submitted)
                {
                    if (bill.Amount <= 5000)
                    {
                        bill.Status = BillStatus.Submitted;
                        Console.WriteLine(string.Format("{0}:{1}已经审核!", this.UserName, bill.BilNo));
                    }
                    else
                    {
                        this.Successor.DoBillOperation(bill);
                    }
    
                }
                else
                {
                    this.Successor.DoBillOperation(bill);
                }
            }
        }
    

    下面来看看经理,在默认构造函数指定下一流程处理者(CEO),同时在业务逻辑中限定只可审核金额低于50000元的单。

    public class Manager : BillHandler
        {
            private List<string> permissions = new List<string>() { "SAVE", "SUBMIT", "AUDIT" };
    
            public Manager(string userName)
            {
                base.UserName = userName;
                base.Permissions = permissions;
                base.Successor = new CEO("CEO--链总");
            }
    
            public override void DoBillOperation(Bill bill)
            {
                if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
                {
                    bill.Status = BillStatus.Saved;
                    Console.WriteLine(string.Format("{0}:{1}已经保存!", this.UserName, bill.BilNo));
                }
                
                if (CheckPermission("SUBMIT") && bill.Status == BillStatus.Saved)
                {
                    bill.Status = BillStatus.Submitted;
                    Console.WriteLine(string.Format("{0}:{1}已经提交!", this.UserName, bill.BilNo));
                }
                
                if (CheckPermission("AUDIT") && bill.Status == BillStatus.Submitted)
                {
                    if (bill.Amount <= 20000)
                    {
                        bill.Status = BillStatus.Submitted;
                        Console.WriteLine(string.Format("{0}:{1}已经审核!", this.UserName, bill.BilNo));
                    }
                    else
                    {
                        this.Successor.DoBillOperation(bill);
                    }
    
                }
                else
                {
                    this.Successor.DoBillOperation(bill);
                }
            }
        }
    

    最后上公司CEO,,注意流程结束了,就不需要指定下一流程处理者了:

    public class CEO : BillHandler
        {
            private List<string> permissions = new List<string>() { "SAVE", "SUBMIT", "AUDIT" };
    
           public CEO(string userName)
            {
                base.UserName = userName;
                base.Permissions = permissions;
            }
    
            public override void DoBillOperation(Bill bill)
            {
                if (CheckPermission("SAVE") && bill.Status == BillStatus.Open)
                {
                    bill.Status = BillStatus.Saved;
                    Console.WriteLine(string.Format("{0}:{1}已经保存!", this.UserName, bill.BilNo));
                }
    
                if (CheckPermission("SUBMIT") && bill.Status == BillStatus.Saved)
                {
                    bill.Status = BillStatus.Submitted;
                    Console.WriteLine(string.Format("{0}:{1}已经提交!", this.UserName, bill.BilNo));
                }
    
                if (CheckPermission("AUDIT") && bill.Status == BillStatus.Submitted)
                {
                    bill.Status = BillStatus.Submitted;
                    Console.WriteLine(string.Format("{0}:{1}已经审核!", this.UserName, bill.BilNo));
                }
            }
        }  
    

    看看测试代码及结果

    static void Main(string[] args)
            {
                PurchaseBill bill = new PurchaseBill()
                {
                    BilNo = "CGDD001",
                    MaterialName = "惠普电脑",
                    Qty = 50,
                    Price = 5000,
                    BillMaker = new Purchaser("采购员--小责")
                    //BillMaker = new Manager("经理--任经理")
                    //BillMaker = new CEO("CEO--链总")
                };
    
                Console.WriteLine(string.Format("创建采购申请单:{0};申请购买{1}台{2}", bill.BilNo, bill.Qty, bill.MaterialName));
    
                bill.BillMaker.HandleBill(bill);
    
                Console.ReadLine();
            }
    
    运行结果

    从中我们可以看到,我们是通过制单员去调用单据处理方法,同时我们可以灵活替换掉制单员,因为实际业务场景也不是限定只有采购员才可以做单的嘛。感兴趣的可以更改数量,看看不同的金额下,最终走了哪些流程。


    优缺点:

    优点:
    责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性。
    缺点
    责任链有两个非常显著的缺点:一是性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。二是调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。

    应用场景:

    流程业务,各司其职,就可以考虑『责任链模式』

    总结:

    『责任链模式』其实是『模板方法模式』的拓展,责任链模式的重点是在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果。

    相关文章

      网友评论

        本文标题:流程业务,各司其职,『责任链模式』

        本文链接:https://www.haomeiwen.com/subject/kikdpttx.html