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
如果內容有誤請多鞭策謝謝