實作責任鍊模式
上課時Bill老師講到責任鍊模式用以封裝if判斷邏輯,心裡想說奇怪了 怎麼會對這個模式完全沒有印象,封裝判斷邏輯只會狀態跟策略模式,回家翻書一看,恩...書上有,一定是之前偷懶沒有實作練習,所以完全沒有印象.......orz
責任鍊模式很像公文系統, 你遞公文上去,每層的主管都會檢查內容是否有誤,內容無誤的話依據公文等級是否需要再往上層遞,直到最後一個權責主管後簽呈結束
abstract class 權責主管
{
protected abstract bool 檢查公文內容是正確的(string 公文);
protected 權責主管 下一位權責主管;
public 權責主管 指定下一位權責主管(權責主管 checker) {
this.下一位權責主管 = checker;
return this.下一位權責主管;
}
public bool 檢查(string 公文) {
if (檢查公文內容是正確的(公文)) {
if (下一位權責主管 != null) {
return 下一位權責主管.檢查(公文);
} else {
return true;
}
} else {
return false;
}
}
}
來看一下從網路上找到的class diagram
http://fox.wikis.com/graphics/chain094.gif
運作上的流程圖
http://fox.wikis.com/graphics/chain089.gif
真的很像linked list, 不過責任鍊不一定是單鍊 , 仍可能有其他變形
從上面的圖來看 每個Handler需要
- 屬於自己業務範圍的判斷邏輯
- 判斷是否需要遞交給下一個Handler(sucessor)
- 指定下一個Handler(sucessor)的能力
以檢查一個長方體的長寬高和體積, 實作一般if else以及使用責任鍊為例
長方體的class
class Cuboid
{
public int Length { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Volume { get { return this.Length * this.Width * this.Height; } }
}
責任鍊的抽象類別, 使用set public來指定下一個successor(checker), 也可以使用constructor或是delegate實作(Bill老師給的delegate例子真的是簡潔漂亮)
abstract class AbstractChecker
{
protected abstract bool InternalCheck(Cuboid cuboid);
protected AbstractChecker _nextChecker;
public AbstractChecker setNextChecker(AbstractChecker checker) {
this._nextChecker = checker;
return this._nextChecker;
}
public bool Check(Cuboid cuboid) {
if (InternalCheck(cuboid)) {
if (_nextChecker != null) {
return _nextChecker.Check(cuboid);
} else {
return true;
}
} else {
Console.WriteLine($"{this.GetType().Name}:false");
return false;
}
}
}
實作責任鍊類別
internal class LengthChecker : AbstractChecker
{
protected override bool InternalCheck(Cuboid cuboid) {
return cuboid.Length > 15;
}
}
internal class WidthChecker: AbstractChecker
{
protected override bool InternalCheck(Cuboid cuboid) {
return cuboid.Width > 10;
}
}
internal class HeightChecker : AbstractChecker
{
protected override bool InternalCheck(Cuboid cuboid) {
return cuboid.Height > 5;
}
}
internal class VolumeChecker: AbstractChecker
{
protected override bool InternalCheck(Cuboid cuboid) {
return cuboid.Volume < 100;
}
}
事前準備完成後, 我們來比較一下使用一般if else跟使用責任鍊的差別
使用一般if else
public static bool CheckCuboid(Cuboid cuboid) {
if (CheckLength(cuboid)) {
if (CheckWidth(cuboid)) {
if (CheckHeight(cuboid)) {
if (CheckVolume(cuboid)) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
使用責任鍊
public static bool CheckCuboidByCOR(Cuboid cuboid) {
AbstractChecker checker = new LengthChecker();
checker.setNextChecker(new WidthChecker())
.setNextChecker(new HeightChecker())
.setNextChecker(new VolumeChecker());
return checker.Check(cuboid);
}
雖然責任鍊的事前準備多了一些, 但是判斷的先後邏輯相當的清晰, 如需擴充更多判斷邏輯也不用加長if else,
更棒的是 如果需要作通用的額外處理,像是Console輸出哪裡判斷false
我可以在抽象類別裡面只要加一行Console.WriteLine($"{this.GetType().Name}:false") 即可得知在哪個實作類別中止並丟出false,
而傳統if else則需要在每一層都手動加入 Console。 在if else多層的情況下,責任鍊是個重構的好選擇