[設計模式] 責任x鏈子模式之不同檔案格式儲存應用範例

  • 194
  • 0

上一篇 [設計模式] 責任鏈模式之變種 - 責任鏈子 將責任與鏈子分開使用,

這篇在將鏈子進行改良,能在開發上更方便使用。

鏈子改良

  1. 實作 IEnumerable<T> 可直接使用 Enumerable 擴充方法
  2. 讓建構式參數可傳入 IEnumerable<T>  直接初始化責任物件
  3. 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();
        }

檔案匯出責任物件

  1. FileData 是給予責任物件的資料
  2. 檔案匯出功能皆實作 FileHandler 
  3. 透過 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 的用意。


參考文章

[設計模式] 責任鏈模式之變種 - 責任鏈子

Chain of Responsibility 模式