流程業務,各司其職,『責任鏈模式』

目錄:設計模式之小試牛刀
源碼路徑: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();
        }
運行結果

從中我們可以看到,我們是通過制單員去調用單據處理方法,同時我們可以靈活替換掉制單員,因為實際業務場景也不是限定只有采購員才可以做單的嘛。感興趣的可以更改數量,看看不同的金額下,最終走了哪些流程。


優缺點:

優點:
責任鏈模式非常顯著的優點是將請求和處理分開。請求者可以不用知道是誰處理的,處理者可以不用知道請求的全貌,兩者解耦,提高系統的靈活性。
缺點
責任鏈有兩個非常顯著的缺點:一是性能問題,每個請求都是從鏈頭遍歷到鏈尾,特別是在鏈比較長的時候,性能是一個非常大的問題。二是調試不很方便,特別是鏈條比較長,環節比較多的時候,由于采用了類似遞歸的方式,調試的時候邏輯可能比較復雜。

應用場景:

流程業務,各司其職,就可以考慮『責任鏈模式』

總結:

『責任鏈模式』其實是『模板方法模式』的拓展,責任鏈模式的重點是在“鏈”上,由一條鏈去處理相似的請求在鏈中決定誰來處理這個請求,并返回相應的結果。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容