C#汎用:Visitorモードのサイクル依存性を解消し、退屈なAcceptメソッド
先日Yuan Ye Pairとこの方法を発見しました
1.クラシックなVisitorモード(サイクル依存)
public interface Document
{
void Accept(DocumentVisitor visitor);
}
public class WordDocument : Document
{
public void Accept(DocumentVisitor visitor)
{
visitor.Visit(this);
}
}
public class ExcelDocument : Document
{
public void Accept(DocumentVisitor visitor)
{
visitor.Visit(this);
}
}
public interface DocumentVisitor
{
void Visit(WordDocument wordDocument);
void Visit(ExcelDocument excelDocument);
}
この中でDocumentはDocumentVisitorに依存し、DocumentVisitorはWordDocumentやExcelDocumentに依存し、WrodDocumentなどはDocumentに依存する
これでDocument->DocumentVisitor->WordDocument->Document->...ループ依存
2.古典的な解依存Visitorモード(Down Cast)
public interface Document
{
void Accept(DocumentVisitor visitor);
}
public class WordDocument : Document
{
public void Accept(DocumentVisitor visitor)
{
if (visitor is WordDocumentVisitor)
{
((WordDocumentVisitor)visitor).Visit(this);
}
}
}
public class ExcelDocument : Document
{
public void Accept(DocumentVisitor visitor)
{
if (visitor is ExcelDocumentVisitor)
{
((ExcelDocumentVisitor)visitor).Visit(this);
}
}
}
public interface DocumentVisitor
{
// ,
}
public class ExcelDocumentVisitor : DocumentVisitor
{
public void Visit(ExcelDocument excelDocument)
{
}
}
public class WordDocumentVisitor : DocumentVisitor
{
public void Visit(WordDocument wordDocument)
{
}
}
3.つまらないAccept()メソッドを排除するVisitorモード
上記のAcceptの方法はほぼ千編一律で、同じパターンに従う.中はタイプ計算にほかならないが、C#の汎用サポートを利用して解消することができる.
public interface Document
{
void Accept(DocumentVisitor visitor);
}
public abstract class VisitableDocument<T> : Document where T : VisitableDocument<T>
{
public void Accept(DocumentVisitor visitor)
{
var interfaces = visitor.GetType().GetInterfaces();
foreach (var type in interfaces)
{
if (type.GetGenericArguments().Contains(typeof(T)))
{
((DocumentVisitor<T>)visitor).Visit((T)this);
}
}
}
}
public class WordDocument : VisitableDocument<WordDocument>
{
// Accept,
}
public class ExcelDocument : VisitableDocument<ExcelDocument>
{
// Accept,
}
public interface DocumentVisitor
{
//
}
public interface DocumentVisitor<T> : DocumentVisitor where T : VisitableDocument<T>
{
// ,
void Visit(T document);
}
public class PrintDocumentVisitor : DocumentVisitor<WordDocument>, DocumentVisitor<ExcelDocument>
{
public void Visit(WordDocument wordDocument)
{
}
public void Visit(ExcelDocument excelDocument)
{
}
}