改用 Double Dispatch 來完成外部實作。
Double Dispatch 是一個有趣的設計方式,中文多半被翻譯成『雙分派』。簡單解釋就是根據雙方的型別來呼叫相對應的方法,實作 Visitor Pattern 時通常會採用這樣的設計。這概念有點彆扭,基本上就是 A 型別有一個 Method ( 暫時稱他為 MethodA )的參數是 B 型別,在 MethodA 裡面呼叫 B 型別的 Method ( 暫時稱為 MethodB ),而 MethodB 裡又有個參數型別是 A 型別;有一種雙方遞迴呼叫的味道在。
依據這樣的概念,我們先建立一個介面 IParseVisitor 公開兩個資料型別的 Parse 方法。
public interface IParseVisitor
{
void Parse(DataForTextFile1 target, string[] items);
void Parse(DataForTextFile2 target, string[] items);
}
依據這個介面實作具體的解析類別。
public class SequenceParseVisitor : IParseVisitor
{
public void Parse(DataForTextFile1 target, string[] items)
{
if (items == null)
{ throw new ArgumentNullException(); }
if (items.Count() < 3)
{ throw new ArgumentException(); }
target.Index = int.Parse(items[0]);
target.English = items[1];
target.Chinese = items[2];
}
public void Parse(DataForTextFile2 target, string[] items)
{
if (items == null)
{ throw new ArgumentNullException(); }
if (items.Count() < 3)
{ throw new ArgumentException(); }
target.Index = int.Parse(items[0]);
target.Person = items[1];
target.WeekDay = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), items[2]);
target.Activity = items[3];
}
}
然後我們必須要為資料型別建立一個 Acceptor 的公開介面,這個介面主要用於對應 IParseVisitor。
public abstract class Acceptor
{
public abstract void Accept(IParseVisitor parser, string[] items);
}
讓兩個資料型別繼承這個抽象類別,並實作 Accept 方法。
public class DataForTextFile1 : Acceptor
{
public int Index { get; set; }
public string English { get; set; }
public string Chinese { get; set; }
public override void Accept(IParseVisitor parser, string[] items)
{
parser.Parse(this, items);
}
}
public class DataForTextFile2 : Acceptor
{
public int Index { get; set; }
public string Person { get; set; }
public DayOfWeek WeekDay { get; set; }
public string Activity { get; set; }
public override void Accept(IParseVisitor parser, string[] items)
{
parser.Parse(this, items);
}
}
各位可以仔細思考一下 Accept 方法的實作內容,Double Dispatch 的實作就是在這的地方。
不能免俗,還是要有個 Console 程式來展示結果。
class Program
{
static void Main(string[] args)
{
ProcessTextFile1();
Console.WriteLine();
Console.WriteLine("**********************************");
Console.WriteLine();
ProcessTextFile2();
Console.ReadLine();
}
static void ProcessTextFile1()
{
var lines = File.ReadAllLines("TextFile1.txt");
List<DataForTextFile1> list = new List<DataForTextFile1>();
foreach (var line in lines)
{
string[] items = line.Split(',');
if (items != null)
{
var obj = new DataForTextFile1();
obj.Accept(new SequenceParseVisitor(), items);
list.Add(obj);
}
}
// 顯示結果
foreach (var item in list)
{
Console.WriteLine(item.Index.ToString());
Console.WriteLine(item.English);
Console.WriteLine(item.Chinese);
Console.WriteLine("---------------------");
}
}
static void ProcessTextFile2()
{
var lines = File.ReadAllLines("TextFile2.txt");
List<DataForTextFile2> list = new List<DataForTextFile2>();
foreach (var line in lines)
{
string[] items = line.Split(',');
if (items != null)
{
var obj = new DataForTextFile2();
obj.Accept(new SequenceParseVisitor(), items);
list.Add(obj);
}
}
// 顯示結果
foreach (var item in list)
{
Console.WriteLine(item.Index.ToString());
Console.WriteLine(item.Person);
Console.WriteLine(item.WeekDay);
Console.WriteLine(item.Activity);
Console.WriteLine("---------------------");
}
}
}