訪問者パターンを理解する


16

GUIコントロールを表すクラスの階層があります。このようなもの:

Control->ContainerControl->Form

さまざまなことを行うオブジェクトで動作する一連のアルゴリズムを実装する必要があり、Visitorパターンが最もクリーンなソリューションになると考えています。たとえば、オブジェクトの階層のXML表現を作成するアルゴリズムを考えてみましょう。「クラシック」アプローチを使用して、私はこれを行います:

public abstract class Control
{
    public virtual XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = document.CreateElement(this.GetType().Name);
        // Create element, fill it with attributes declared with control
        return xml;
    }
}

public abstract class ContainerControl : Control
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Use forech to fill XmlElement with child XmlElements
        return xml;
    }
}

public class Form : ContainerControl
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Fill remaining elements declared in Form class
        return xml;
    }
}

しかし、ビジターパターンでこれを行う方法はわかりません。これは基本的な実装です。

public class ToXmlVisitor : IVisitor
{
    public void Visit(Form form)
    {
    }
}

抽象クラスも実装に役立つので、ToXmlVisitorでそれを適切に行う方法がわかりませんか?

訪問者パターンを検討している理由は、一部のアルゴリズムではクラスが実装されているプロジェクトでは利用できない参照が必要であり、多数の異なるアルゴリズムがあるため、大きなクラスを避けるためです。


あなたの質問は何ですか?
gnat

基本的に、訪問者パターンを使用してToXml()メソッドを書き換える方法。
ネズレリ


リンクをありがとう。動的なディスパッチは、従来の訪問者パターンを単純化しますが、あまり変わりません。
ネズレリ

@Nezreliはい、そうです。扱うWindowsフォームコントロールなど、Visitorパターンをサポートしないクラスで動作します。
クリスヴァンダー

回答:


17

ビジターパターンは、単一バインディングのみをサポートするプログラミング言語でデュアルバインディングをシミュレートするメカニズムです。残念ながら、その声明は物事をあまり明確にしていないかもしれないので、簡単な例を使って説明しましょう。

使用しているプラ​​ットフォームである.NETおよびC#では、ToString()関数を使用してオブジェクトを文字列に変換できます。その機能が実行すること、つまり実行されるコードは、それを適用するオブジェクトのタイプ(仮想メソッド)によって異なります。実行されるコードは、オブジェクトの1つのタイプに依存するため、使用されるメカニズムはシングルバインディングと呼ばれます。

しかし、オブジェクトの種類ごとに、オブジェクトを文字列に変換する複数の方法が必要な場合はどうでしょうか?オブジェクトを文字列に変換する2つの方法が必要な場合、実行されるコードは2つのことに依存します:変換されるオブジェクトだけでなく、変換方法も必要ですか?

二重バインディングがあれば、うまく解決できます。ただし、C#を含むほとんどのオブジェクト指向言語は、単一のバインディングのみをサポートします。

ビジターパターンは、デュアルバインディングを2つの連続したシングルバインディングに変えることで問題を解決します。

上記の例では、オブジェクトの仮想メソッドを使用して変換し、変換アルゴリズムを実装するオブジェクトの2番目の仮想メソッドを呼び出します。

ただし、これは、アルゴリズムを適用するオブジェクトがこれと連携する必要があることを意味します。つまり、ビジターパターンをサポートする必要があります。

訪問者パターンをサポートしていない.NETのWindows Formsクラスを使用しているようです。より具体的には、彼らはpublic virtual void Accept(IVisitor)メソッドを持っている必要がありますが、明らかに持っていません。

それでは、代替手段は何ですか?さて、.NETはシングルバインディングをサポートするだけでなく、ダイナミックバインディングもサポートします。これは、デュアルバインディングよりも強力です。

問題を解決できるようにするためのテクニックの適用方法の詳細については(よく理解している場合)、Farewell Visitorをご覧ください。

更新:

特定の問題に手法を適用するには、最初に拡張メソッドを定義します。

public static XmlDocument ToXml(this Control control)
{
    XmlDocument xml = new XmlDocument();
    XmlElement root = xml.CreateElement(control.GetType().Name);
    xml.AppendChild(root);

    Visit(control, xml, root);

    return xml;
}

動的ディスパッチャを作成します。

private static void Visit(Control control, XmlDocument xml, XmlElement root)
{
    dynamic dynamicControl = control; //notice the 'dynamic' type.
                                      //this is the key to dynamic dispatch

    VisitCore(dynamicControl, xml, root);
}

次に、特定のメソッドを入力します。

private static void VisitCore(Control control, XmlDocument xml, XmlElement root)
{
    // TODO: specific Control handling
}

private static void VisitCore(ContainerControl control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as Control, xml, root);

    // TODO: specific ContainerControl handling
    // for example:
    foreach (Control child in control.Controls)
    {
        XmlElement element = xml.CreateElement(child.GetType().Name);
        root.AppendChild(element);

        // call the dynamic dispatcher method
        Visit(child, xml, element);
    }
}

private static void VisitCore(Form control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as ContainerControl, xml, root);

    // TODO: specific Form handling
}

.NETでの動的ディスパッチは確かに非常に強力です。しかし、私はそれが少し...まあ..遅いかもしれないことに気付きましたが、それは複数のクラスとビジターとのインターフェースで数行をとるコードの単一行で行います
Newtopian

それでも、ToXmlアルゴリズムでは、継承チェーンのすべての型を「訪問」する必要があるため、動的ディスパッチでは問題を解決できません。私の例では、XML変換を成功させるために、Control、ContainterControl、およびFormをこの順序で訪問する必要があります。
ネズレリ

@Nezreliそれはあなたの問題を解決することができます、私は方法を示すために私の答えを更新しました。
クリスヴァンダー

動的変数の定義にコメントを追加する許可を得ました。コードを見つけるまでに2回読み取りましたが、これがストーリー全体の鍵です。
クリスティディアコネスク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.