「増補改訂版Java言語で学ぶデザインパターン入門」の「Visitorパターン」をC#で書いてみました。
GitHubにもコードを置いています。
参考
DesignPattern【Visitor】GitHub
コード
public abstract class Visitor
{
public abstract void Visit(File file);
public abstract void Visit(Directory directory);
}
public interface IElement
{
abstract void Accept(Visitor v);
}
public abstract class Entry : IElement
{
public abstract string GetName();
public abstract int GetSize();
public abstract Entry Add(Entry entry);
public abstract IEnumerator Entry> Iterator();
public override string ToString()
{
return $"{GetName()}({GetSize()})";
}
public abstract void Accept(Visitor v);
}
public class File : Entry
{
private string name;
private int size;
public File(string name, int size)
{
this.name = name;
this.size = size;
}
public override Entry Add(Entry entry)
{
throw new FileTreatmentException();
}
public override string GetName()
{
return name;
}
public override int GetSize()
{
return size;
}
public override IEnumerator Entry> Iterator()
{
throw new FileTreatmentException();
}
public override void Accept(Visitor v)
{
v.Visit(this);
}
}
public class Directory : Entry
{
private string name;
private IList Entry> dir = new List Entry>();
public Directory(string name)
{
this.name = name;
}
public override Entry Add(Entry entry)
{
dir.Add(entry);
return this;
}
public override string GetName()
{
return name;
}
public override int GetSize()
{
int size = 0;
var it = dir.GetEnumerator();
while (it.MoveNext())
{
size += it.Current.GetSize();
}
return size;
}
public override IEnumerator Entry> Iterator()
{
return dir.GetEnumerator();
}
public override void Accept(Visitor v)
{
v.Visit(this);
}
public override string ToString()
{
return $"{GetName()}({GetSize()})";
}
}
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 savefir = currentdir;
currentdir = $"{currentdir}/{directory.GetName()}";
var it = directory.Iterator();
while (it.MoveNext())
{
it.Current.Accept(this);
}
currentdir = savefir;
}
}
public class FileTreatmentException : Exception
{
public FileTreatmentException() : base()
{
}
public FileTreatmentException(string message) : base(message)
{
}
public FileTreatmentException(string message, Exception innerException)
: base(message, innerException)
{
}
}
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Maling 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", 1000));
bindir.Add(new File("latex", 2000));
rootdir.Accept(new ListVisitor());
Console.WriteLine("");
Console.WriteLine("Making usr 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("junc.mail", 500));
rootdir.Accept(new ListVisitor());
}
catch (FileTreatmentException ex)
{
Console.WriteLine(ex);
}
}
}
実行結果は以下のようになります。
Making root entries...
/root(3000)
/root/bin(3000)
/root/bin/vi(1000)
/root/bin/latex(2000)
/root/tmp(0)
/root/usr(0)
Making usr entries...
/root(4500)
/root/bin(3000)
/root/bin/vi(1000)
/root/bin/latex(2000)
/root/tmp(0)
/root/usr(1500)
/root/usr/yuki(300)
/root/usr/yuki/diary.html(100)
/root/usr/yuki/Composite.java(200)
/root/usr/hanako(300)
/root/usr/hanako/memo.tex(300)
/root/usr/tomura(900)
/root/usr/tomura/game.doc(400)
/root/usr/tomura/junc.mail(500)
その他のデザインパターンは以下の記事から確認してください。