C#デザインパターン  - Visitor

ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。
解説らしい解説は載せていませんので、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』も合わせてお読みください。

※当ソースは、Visual C# 2008 Express Editionで動作を確認しています。
  Visual Studio 2005をお使いの方は、ブログの記事を残しておきますので、そちらをご覧ください。



■IElement.cs
using System;

namespace Gushwell.DesignPatterns {

    public interface IElement {
        void Accept(Visitor v);
    }
}


■Entry.cs
using System;
using System.Collections.Generic;

namespace Gushwell.DesignPatterns {

    public abstract class Entry : IElement { 
        public abstract string Name { get; }
        public abstract int Size { get; }

        // エントリを追加する
        public virtual Entry Add(Entry entry) {
            throw new FileTreatmentException();
        }

        // 文字列表現
        public override string ToString() {
            return Name + " (" + Size + ")";
        }

        public abstract void Accept(Visitor param1);

    }
}


■File.cs
using System;

namespace Gushwell.DesignPatterns {

    public class File : Entry {
        private string name;
        private int size;

        public File(string name, int size) {
            this.name = name;
            this.size = size;
        }

        public override string Name {
            get { return name; }

        }

        public override int Size {
            get { return size; }

        }

        public override void Accept(Visitor v) {
            v.Visit(this);
        }
    }
}


■Directory.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace Gushwell.DesignPatterns {

    public class Directory : Entry, IEnumerable<Entry> {
        private string name;                         // ディレクトリの名前
        private List<Entry> dir = new List<Entry>(); // ディレクトリエントリの集合

        // コンストラクタ
        public Directory(string name) {
            this.name = name;
        }

        // 名前を得る
        public override string Name {
            get { return name; }

        }

        // サイズを得る
        public override int Size {
            get {
                //int size = 0;
                //foreach (Entry entry in dir)
                //    size += entry.Size;
                //return size;
                //上記処理をAggregateを使って書いてみた。
                return dir.Aggregate(0, (a, entry) => a + entry.Size);
            }

        }

        // エントリの追加
        public override Entry Add(Entry entry) {
            dir.Add(entry);
            return this;
        }

        // 訪問者の受け入れ
        // 受け入れたときに何をするかは、Visitorが知っている。
        public override void Accept(Visitor v) {
            v.Visit(this);
        }

        public IEnumerator<Entry> GetEnumerator() {
            return dir.GetEnumerator();
        }


        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return this.GetEnumerator();
        }
    }
}



■Visitor.cs
using System;

namespace Gushwell.DesignPatterns {

    public abstract class Visitor {
        public abstract void Visit(File file);
        public abstract void Visit(Directory directory);
    }
}


■ListVisitor.cs
using System;
using System.Linq;
using Gushwell.Dejavu;

namespace Gushwell.DesignPatterns {

    public class ListVisitor : Visitor {
        private string currentdir = ""; // 現在注目しているディレクトリ名

        // ファイルを訪問したときに呼ばれる
        public override void Visit(File file) {
            Console.WriteLine(currentdir + "/" + file);
        }

        // ディレクトリを訪問したときに呼ばれる
        public override void Visit(Directory directory) {
            Console.WriteLine(currentdir + "/" + directory);
            string savedir = currentdir;
            currentdir = currentdir + "/" + directory.Name;
            // Directoryは、IEnumerable<Entry>を実装している。
            // directory配下のすべてのEntryを受け入れる。
            directory.ForEach(e => e.Accept(this));
            currentdir = savedir;
        }
    }
}




■FileTreatmentException.cs
using System;

namespace Gushwell.DesignPatterns {

    [Serializable]
    public class FileTreatmentException : System.SystemException {
        public FileTreatmentException() {
        }
        public FileTreatmentException(string msg)
            : base(msg) {
        }
    }
}


■Program.cs
using System;

namespace Gushwell.DesignPatterns {

    public class Program {
        [STAThread]
        public static void Main(string[] args) {
            try {
                Console.WriteLine("Making root entries...");
                Directory rootdir = new Directory("root");
                Directory bindir = new Directory("bin");
                Directory tmpdir = new Directory("tmp");
                Directory usrdir = new Directory("usr");
                rootdir.Add(bindir);
                rootdir.Add(tmpdir);
                rootdir.Add(usrdir);
                bindir.Add(new File("vi", 10000));
                bindir.Add(new File("latex", 20000));
                rootdir.Accept(new ListVisitor());

                Console.WriteLine("");
                Console.WriteLine("Making user entries...");
                Directory yuki = new Directory("yuki");
                Directory hanako = new Directory("hanako");
                Directory tomura = new Directory("tomura");
                usrdir.Add(yuki);
                usrdir.Add(hanako);
                usrdir.Add(tomura);
                yuki.Add(new File("diary.html", 100));
                yuki.Add(new File("Composite.java", 200));
                hanako.Add(new File("memo.tex", 300));
                tomura.Add(new File("game.doc", 400));
                tomura.Add(new File("junk.mail", 500));
                rootdir.Accept(new ListVisitor());
            } catch (FileTreatmentException e) {
                Console.Error.WriteLine(e.StackTrace);
            }
        }
    }
}



■EnumerableExtentions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Gushwell.Dejavu {
    public static class EnumerableExtentions {
        public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) {
            foreach (var x in source) {
                action(x);
            }
        }
    }
}