一般我們在處理資料時,如果有多種類型資料就要判斷資料類型來決定處理的方式,最簡單的方式就是用一堆if...else,但這樣不僅看起來很醜,而且邏輯一變動就要改一拖拉庫。這時候就可以使用Chain of Responsibility,讓處理方法自己決定是否要處理,由被動變成主動,增加程式的獨立性。
目的
為了降低Client與Handler之間的Coupling,不用特別指定哪個Handler處理,由Handler決定是否要處理。動機
在程式需要根據資料類型進行不同作業的情況下,一般來說Client會利用if...else或switch來指定特定的Handler處理,這樣是會造成Client與Handler Coupling太高,如果要增加或減少Handler或改變Handler邏輯時就比較麻煩。使用時機
- 當你有多個Handler,要在執行期間各種處理方式自己決定是否要執行。
- 當你發出一個Request,而不需要指定特定的Handler。
結構
Chain of Responsibility主要精隨為一個抽象Handler類別,這個抽象Handler定義了一個HandleRequest方法,各具象類別須根據自身邏輯來實作這個方法。另外,抽象Handler內要包含一個抽象Handler的屬性(successor),作為下一個傳遞的對象。Chain of Responsibility執行流程為為,先產生第一個具象Handler實體,然後依優先順續排序傳入下一個具象Handler的實體,以此類推。每個Handler在執行HandleRequest時先判斷自己能不能處理,如果不能就傳給下一個(successor),直到找到適合的Handler或都沒找到。
實作
限量在下面以一個檔案格式Parser的範例來說明Chain of Responsibility,如上圖。因為檔案有許多種格式,要取出其資料必須先按照各種格式的規範來解析檔案,像XML, JSON, CSV格式盡不相同,在未知的檔案格式的情況下,必須要提供多種格式的Parser,再根據進來檔案的格式來決定使用哪個Parser。這種情境就可以使用到Chain of Responsibility,我們可以先將各種可能用到的Parser利用Chain of Responsibility的特性串接起來,執行時會一層一層的串遞,各Parser接到檔案判斷自己是否能處理再處理,如果不行在傳給下一層的Parser,以此類推。 以下為範例程式碼:
using System;
using System.IO;
namespace TestSolution
{
class Program
{
static void Main(string[] args)
{
// 產生Parser實體,在按照優先順序加入其他Parser實體
var parser =
new XMLFileParser(
new JSONFileParser(
new CSVFileParser(null)
)
);
var dir = new DirectoryInfo("./files");
foreach(var file in dir.GetFiles())
{
// 針對每個檔案Parse
var result = parser.Parse(file);
Console.WriteLine(
string.Format("FileName: {0}, Result: {1}", file.Name, result)
);
}
Console.Read();
}
}
///
/// 抽象類別,即Handler
///
public abstract class FileParser
{
protected FileParser _successor;
public FileParser(FileParser parser)
{
_successor = parser;
}
public abstract string Parse(FileInfo file);
}
///
/// 具象類別,針對XML格式檔案,即ConcreteHandler
///
public class XMLFileParser : FileParser
{
public XMLFileParser(FileParser parser) : base(parser) { }
public override string Parse(FileInfo file)
{
var result = string.Empty;
if (file.Extension.Equals(".xml", StringComparison.OrdinalIgnoreCase))
{
// 解析XML
result = "XML Result";
}
else if (_successor == null)
{
result = "No suitable parser";
}
else
{
result = _successor.Parse(file);
}
return result;
}
}
///
/// 具象類別,針對JSON格式檔案,即ConcreteHandler
///
public class JSONFileParser : FileParser
{
public JSONFileParser(FileParser parser) : base(parser) { }
public override string Parse(FileInfo file)
{
var result = string.Empty;
if (file.Extension.Equals(".json", StringComparison.OrdinalIgnoreCase))
{
// 解析JSON
result = "JSON Result";
}
else if (_successor == null)
{
result = "No suitable parser";
}
else
{
result = _successor.Parse(file);
}
return result;
}
}
///
/// 具象類別,針對CSV格式檔案,即ConcreteHandler
///
public class CSVFileParser : FileParser
{
public CSVFileParser(FileParser parser) : base(parser) { }
public override string Parse(FileInfo file)
{
var result = string.Empty;
if (file.Extension.Equals(".csv", StringComparison.OrdinalIgnoreCase))
{
// 解析CSV
result = "CSV Result";
}
else if (_successor == null)
{
result = "No suitable parser";
}
else
{
result = _successor.Parse(file);
}
return result;
}
}
}
從上面執行結果可以看到,程式依序讀取V.html, W.csv, X.json, Z.xml檔案並Parse,因為沒有實作HTML的Parser,所以V.html檔案沒有Parse到,而其他檔案都有找到對應的Parser。
優點
- 可降低Client與各Handler之間的Coupling。
- 可動態增加或減少Handler,增加了彈性。
相關樣式
使用頻率
參考來源:
Design Patterns - Gang of Four
dofactory - Chain of Responsibility



留言
張貼留言