2006年02月27日

Visitor -構造を渡り歩きながら仕事をする

  
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



FileTreatmentExceptionクラス
using System;

namespace Gushwell.DesignPatterns {

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


Visitorクラス
using System;

namespace Gushwell.DesignPatterns {

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


IElementインターフェース
using System;

namespace Gushwell.DesignPatterns {

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


Entryクラス
using System;
using System.Collections;

namespace Gushwell.DesignPatterns {

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

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

// IEnumeratorの生成
public virtual IEnumerator GetEnumerator() {
throw new FileTreatmentException();
}

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

public abstract void Accept(Visitor param1);
}
}


Fileクラス
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クラス
using System.Collections.Generic;

namespace Gushwell.DesignPatterns {

public class Directory : Entry, IEnumerable {
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;
}
}

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

// 訪問者の受け入れ
public override void Accept(Visitor v) {
v.Visit(this);
}

// IEnumeratorの生成
public override IEnumerator GetEnumerator() {
return dir.GetEnumerator();
}
}
}


ListVisitorクラス
using System;

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;

foreach (Entry entry in directory)
entry.Accept(this);

currentdir = savedir;
}
}
}


Programクラス
using System;

namespace Gushwell.DesignPatterns {

public class Program {
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);
}
}
}
}



 

この記事へのトラックバックURL