上一篇 [設計模式] 責任鏈模式之變種 - 責任鏈子 將責任與鏈子分開使用,
這篇在將鏈子進行改良,能在開發上更方便使用。
鏈子改良
- 實作 IEnumerable<T> 可直接使用 Enumerable 擴充方法
- 讓建構式參數可傳入 IEnumerable<T> 直接初始化責任物件
- Execute 改可回傳有執行的物件,可後續用於判斷
public class Chain<T> : IEnumerable<T>
{
private IList<T> _chain = null;
public Chain() : this(Enumerable.Empty<T>()) { }
public Chain(IEnumerable<T> collection)
{
_chain = new List<T>(collection);
}
public Chain<T> SetNext(T item)
{
_chain.Add(item);
return this;
}
public T Execute(Func<T, bool> checkAction) => _chain.FirstOrDefault(checkAction);
public IEnumerator<T> GetEnumerator() => _chain.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _chain.GetEnumerator();
}
檔案匯出責任物件
- FileData 是給予責任物件的資料
- 檔案匯出功能皆實作 FileHandler
- 透過 CheckSave 定義如果副檔名符合的話,就為該責任物件處理儲存
public class FileData
{
public string FileName { get; set; }
public string[] Data { get; set; }
}
public abstract class FileHandler
{
public abstract string Extension { get; }
protected abstract void Save(FileData fileData);
public virtual bool CheckSave(FileData fileData)
{
var isMatch = Path.GetExtension(fileData.FileName).Equals(Extension, StringComparison.OrdinalIgnoreCase);
if (isMatch)
Save(fileData);
return isMatch;
}
}
public class CsvFileHandler : FileHandler
{
public override string Extension => ".csv";
protected override void Save(FileData fileData)
{
var text = string.Join(",", fileData.Data);
File.WriteAllText(fileData.FileName, text);
}
}
public class XmlFileHandler : FileHandler
{
public override string Extension => ".xml";
protected override void Save(FileData fileData)
{
var doc = new XmlDocument();
doc.LoadXml("<root></root>");
foreach (var item in fileData.Data)
{
var elem = doc.CreateElement("Data");
elem.InnerText = item;
doc.DocumentElement.AppendChild(elem);
}
doc.Save(fileData.FileName);
}
}
使用方式
static void Main(string[] args)
{
var fileChain = new Chain<FileHandler>()
.SetNext(new XmlFileHandler())
.SetNext(new CsvFileHandler());
var fileData = new FileData()
{
FileName = "測試檔案名稱.xml",
Data = Enumerable.Range(1, 10).Select(i => i.ToString()).ToArray()
};
var fileHandler = fileChain.Execute(x => x.CheckSave(fileData));
if (fileHandler != null)
Console.WriteLine($"已儲存{fileHandler.Extension}副檔名");
}
輸出結果
XML:
<root>
<Data>1</Data>
<Data>2</Data>
<Data>3</Data>
<Data>4</Data>
<Data>5</Data>
<Data>6</Data>
<Data>7</Data>
<Data>8</Data>
<Data>9</Data>
<Data>10</Data>
</root>
多個責任物件都想執行時
原本很純的責任鏈模式應該會由某個責任物件來停止繼續往下傳遞,
但現有需求是匯出某幾種格式的檔案,提供給不同使用者自由解析時,
原本的代碼只需要將:
var fileHandler = fileChain.Execute(x => x.CheckSave(fileData));
改為就能匯出多個符合責任的檔案格式:
foreach (var f in fileChain)
f.CheckSave(fileData);
也是為什麼要額外實作 IEnumerable 的用意。