今天看到 wikipedia 的責任鏈模式,好奇之下搜尋相關文章,此文章主要參考
91大的 [.NET]重構之路系列v11 –用責任鏈模式打破討厭的switch case
的變種作法,所以跟原本的責任鏈模式的設計思路不太相同,但結果相同,
所以閱讀前,建議先看完91大的文章後,在來看這篇會比較能感覺出差異。
這邊先引用91大文章結尾結論中的補充來複習一下責任鏈的重點,
20120415 補充:責任鏈的用意在於把每個角色的職責分清楚,每個物件只需要做好自己的事,透過責任鏈的方式來組合,並完成使用場景的需求。各角色的職責如下:
- 鏈上的每個責任物件,只知道『針對什麼情況,代表是自己的職責』,以及只知道『自己的職責,應該要做什麼事』。
- 抽象的責任物件,只知道『往下傳遞』這件事。
- 使用場景,只知道『呼叫這條責任鏈處理』,就可以得到想要的結果。
責任
第2點也是我想改變的部分,如果系統中真的有很多地方方需要用到責任鏈模式時,
我會看到一堆的抽象責任物件做負責往下傳遞的這件事情,
所以我希望每個責任物件只做好自己要做什麼事情,還有是不是自己的責任就好,
所以每個責任物件沒有繼承抽象責任物件的往下傳遞這個的相關代碼:
public interface IMonkey
{
bool DoSomething(DayOfWeek today);
}
public abstract class AbstractMonkey : IMonkey
{
public virtual bool DoSomething(DayOfWeek today)
{
// 是自己的責任,就做
var result = IsMyResponsibility(today);
if (result)
MyAction(today);
return result;
}
protected abstract void MyAction(DayOfWeek today);
protected abstract bool IsMyResponsibility(DayOfWeek today);
}
public class MonkeyMonday : AbstractMonkey
{
protected override void MyAction(DayOfWeek today)
{
Console.WriteLine("星期一,猴子穿新衣");
}
protected override bool IsMyResponsibility(DayOfWeek today)
{
return today == DayOfWeek.Monday;
}
}
public class MonkeyTuesday : AbstractMonkey
{
protected override void MyAction(DayOfWeek today)
{
Console.WriteLine("星期二,猴子肚子餓");
}
protected override bool IsMyResponsibility(DayOfWeek today)
{
return today == DayOfWeek.Tuesday;
}
}
public class MonkeySunday : AbstractMonkey
{
protected override void MyAction(DayOfWeek today)
{
Console.WriteLine("星期日,猴子過生日");
}
protected override bool IsMyResponsibility(DayOfWeek today)
{
return today == DayOfWeek.Sunday;
}
}
從上面看到 AbstractMonkey 只單純使用模板方法,要求實作的猴子都要實作 MyAction 與 IsMyResponsibility,
而 AbstractMonkey 實作 IMonkey.DoSomething 做某些事情的流程,並回傳是否有做了。
鏈子
上面那些代碼,只做了責任物件應該做什麼事情,也就是責任有了,那接著我就要做一個鏈子:
public class Chain<T>
{
private IList<T> _chain = new List<T>();
public Chain<T> SetNext(T item)
{
_chain.Add(item);
return this;
}
public void Execute(Func<T, bool> doSomething)
{
foreach (var item in _chain)
{
if (doSomething(item))
break;
}
}
}
鏈子的用意就是『往下傳遞』,所以有 SetNext 可設定下一個責任物件,然後使用 Execute 的委派參數來檢查某一個物件的回傳是否停止做。
使用方式
static void Main(string[] args)
{
var today = DateTime.Today.DayOfWeek;
var monkeyChain = new Chain<IMonkey>()
.SetNext(new MonkeyMonday())
.SetNext(new MonkeyTuesday())
.SetNext(new MonkeySunday());
monkeyChain.Execute(monkey => monkey.DoSomething(today));
}
先用 SetNext 將 IMonkey 的實作串在一起,在透過 IMonkey.DoSomething 執行責任鏈。
執行結果
小結
這個是責任鏈的變種,主要拆成責任跟鏈子,
為了要在使用責任鏈時,讓責任鏈寫起來更無負擔,
以下是重複使用鏈子後,能減少的代碼(引用91大的部分代碼作為範例):
1. 減少寫使用建構式(或方法)設定下一個責任物件的代碼。
public AbstractMonkey(AbstractMonkey monkey)
{
this._nextMonkey = monkey;
}
2. 減少寫儲存下一個責任物件欄位私有欄位代碼。
private AbstractMonkey _nextMonkey;
3. 減少寫如何執行下一個責任物件的代碼。
public void DoSomething(DayOfWeek today)
{
if (isMyResponsibility(today))
{
this.MyAction(today);
}
else
{
if (this._nextMonkey != null)
{
this._nextMonkey.DoSomething(today);
}
else
{
Console.WriteLine("責任鏈結束!");
}
}
}