[私人筆記] - 責任鏈模式

Design pattern - Chain of Responsibility
這也是比較偏 寫給自己看的私人筆記 並不會做太多解釋

整理一下 遇到用 很多狀態需要檢查的時候 怎樣用 責任鏈模式 讓職責切開來
情境一 ( 實務上不會這樣簡單 這裡就是拿來記憶 )

if (!user.Identity.StartsWith("A"))
{
    return false;
}
else if (user.Age < 18)
{
    return false;
}
else if (user.Height < 180 && user.Weight > 90)
{
    return false;
}
else
{
    return true;
}

符合條件的會員就是VIP 
 責任鏈模式 一

namespace DesignPatternTraining._02_COR
{
    public class SituationC_COR
    {

        public bool IsVIP(CorUser user)
        {
            var checker = VIPCheckerContext.GetCheckers();
            return checker.Check(user);
        }
    }

    public class VIPCheckerContext
    {
        public static VIPChecker GetCheckers()
        {
            //不用建構子 建立SetNext去指定下一個VIPChecker
            VIPChecker identityChecker = new IdentityChecker();
            identityChecker.SetNext(new AgeChecker())
                           .SetNext(new HeightWeightChecker());
            
            return identityChecker;
        }
    }

    public abstract class VIPChecker
    {
        protected VIPChecker _checker;
        public VIPChecker SetNext(VIPChecker checker)
        {
            _checker = checker;
            return _checker;
        }

        protected abstract bool InternalCheck(CorUser user);
        public bool Check(CorUser user)
        {
            if (InternalCheck(user))
            {
                // 檢查結果為 false, 則跳出, 不再往下處理
                return false;
            }

            if (_checker == null)
            {
                //若沒有後繼者,表示檢查結束
                return true;
            }

            //有後繼者則繼續往下處理
            return _checker.Check(user);
        }

       
    }

    public class IdentityChecker : VIPChecker
    {
        protected override bool InternalCheck(CorUser user)
        {
            return !user.Identity.StartsWith("A");
        }
    }

    public class AgeChecker : VIPChecker
    {
        protected override bool InternalCheck(CorUser user)
        {
            return user.Age < 18;
        }
    }

    public class HeightWeightChecker : VIPChecker
    {
        protected override bool InternalCheck(CorUser user)
        {
            return user.Height < 180 && user.Weight > 90;
        }
    }

}

責任鏈有令我最頭痛的其實是 他加入檢察條件的時候 該怎樣寫
用建構子 一層包一層 或是 給一個設定下一層的方法 都可以 就看團隊覺得哪一個看得懂為主

//這樣寫的好處 直覺先檢查 Identity -> Age -> HeightWeight
//缺點 很多時 程式碼會很長很多括號
return new IdentityChecker(new AgeChecker(new HeightWeightChecker(null)));

//這樣寫的好處 程式碼都會比較短 變成很多行 比較好看
//缺點 檢查順序 是由下到上 不小心會搞混
VIPChecker heightWeightChecker = new HeightWeightChecker(null);
VIPChecker ageChecker = new AgeChecker(heightWeightChecker);
return new IdentityChecker(ageChecker);

//不用建構子 建立SetNext去指定下一個VIPChecker
VIPChecker identityChecker = new IdentityChecker();
identityChecker.SetNext(new AgeChecker())
               .SetNext(new HeightWeightChecker());
            
return identityChecker;

我覺得 SetNext 閱讀最清楚 只是回傳的物件 已經替換為新的檢察條件 要注意
不能直接這樣寫
 return identityChecker.SetNext(new AgeChecker())
                                    .SetNext(new HeightWeightChecker());

這樣會是回傳 HeightWeightChecker !! 

  責任鏈模式 二
集合的概念把 檢查方法放到集合裡面 再一個個取出來檢查
設定的方式也是可以用建構子 或是 設定 加入查檢條件的方法 

namespace DesignPatternTraining._02_COR
{
    public class SituationC_COR2
    {
        public bool IsVIP(CorUser user)
        {
            var handler = VIPCheckerContext2.GetHandlers();
            return handler.Handle(user);
        }
    }

    public class VIPCheckerContext2
    {
        public static VIPHandler GetHandlers()
        {
            var handler = new VIPHandler();
            handler.SetNext(new IdentityHandler());
            handler.SetNext(new AgeHandler());
            handler.SetNext(new HeightHandler());
            return handler;
        }
    }

    public interface IReceiver<T> where T : class
    {
        bool Handle(T request);
    }

    public class VIPHandler
    {
        private readonly IList<IReceiver<CorUser>> receivers;

        public VIPHandler()
        {
            receivers = new List<IReceiver<CorUser>>();
        }

        public bool Handle(CorUser user)
        {
            if (!receivers.Any()) return false;

            foreach (var receiver in receivers)
            {
                Console.WriteLine($"Running: {receiver.GetType().Name}");

                if (receiver.Handle(user))
                {
                    return false;
                }
            }
            return true;
        }

        public void SetNext(IReceiver<CorUser> next)
        {
            receivers.Add(next);
        }
    }


    public class IdentityHandler : IReceiver<CorUser>
    {
        public bool Handle(CorUser user)
        {
            return !user.Identity.StartsWith("A");
        }
    }

    public class AgeHandler : IReceiver<CorUser>
    {
        public bool Handle(CorUser user)
        {
            return user.Age < 18;
        }
    }

    public class HeightHandler : IReceiver<CorUser>
    {
        public bool Handle(CorUser user)
        {
            return user.Height < 180 && user.Weight > 90;
        }
    }

}

程式碼 

參考資料
C# Design Patterns: Chain of Responsibility

如果內容有誤請多鞭策謝謝