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)
        {
        }
    }