字串陣列解析小品集 (5) 外部實作法 (Double Dispatch)

改用 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("---------------------");
            }
        }
    }