Memahami Pola Pengunjung

16

Saya memiliki hierarki kelas yang mewakili kontrol GUI. Sesuatu seperti ini:

Control->ContainerControl->Form

Saya harus mengimplementasikan serangkaian algoritme yang bekerja dengan objek melakukan berbagai hal dan saya berpikir bahwa pola Pengunjung akan menjadi solusi terbersih. Mari kita ambil contoh algoritma yang membuat representasi Xml dari hierarki objek. Dengan menggunakan pendekatan 'klasik' saya akan melakukan ini:

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;
    }
}

Tapi saya tidak yakin bagaimana melakukan ini dengan pola pengunjung. Ini adalah implementasi dasar:

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

Karena bahkan kelas abstrak membantu implementasi, saya tidak yakin bagaimana melakukannya dengan benar di ToXmlVisitor?

Alasan saya mempertimbangkan pola Pengunjung adalah bahwa beberapa algoritma akan memerlukan referensi yang tidak tersedia di proyek di mana kelas diimplementasikan dan ada sejumlah algoritma yang berbeda jadi saya menghindari kelas besar.

Nezreli
sumber
apa pertanyaan Anda?
nyamuk
Pada dasarnya cara menulis ulang metode ToXml () menggunakan pola pengunjung.
Nezreli
Terima kasih untuk tautannya. Pengiriman dinamis menyederhanakan pola pengunjung tradisional tetapi tidak banyak berubah.
Nezreli
@Nezreli Ya. Ini berfungsi dengan kelas yang tidak mendukung pola Pengunjung, seperti kontrol Formulir Windows yang Anda hadapi.
Kris Vandermotten

Jawaban:

17

Pola pengunjung adalah mekanisme untuk mensimulasikan penjilidan rangkap dalam bahasa pemrograman yang hanya mendukung penjilidan tunggal. Sayangnya, pernyataan itu mungkin tidak menjelaskan banyak hal, jadi izinkan saya menjelaskan dengan contoh sederhana.

Di .NET dan C #, platform yang Anda gunakan, objek dapat dikonversi menjadi string menggunakan ToString()fungsi. Apa yang dilakukan funtion, yaitu kode yang dieksekusi, tergantung pada jenis objek yang Anda terapkan (ini adalah metode virtual). Kode apa yang dieksekusi tergantung pada satu hal, satu jenis objek, maka mekanisme yang digunakan disebut pengikatan tunggal.

Tetapi bagaimana jika saya ingin memiliki lebih dari satu cara untuk mengubah objek menjadi string, untuk setiap jenis objek yang berbeda? Bagaimana jika saya ingin memiliki dua cara untuk mengubah objek menjadi string, sehingga kode yang dieksekusi tergantung pada dua hal: tidak hanya objek yang akan dikonversi, tetapi juga cara kita ingin dikonversi?

Itu bisa diselesaikan dengan baik jika kita memiliki ikatan ganda. Tetapi sebagian besar bahasa OO, termasuk C #, hanya mendukung pengikatan tunggal.

Pola pengunjung menyelesaikan masalah, dengan mengubah ikatan ganda menjadi dua ikatan tunggal berturut-turut.

Dalam contoh kami di atas, itu akan menggunakan metode virtual di objek untuk mengkonversi, yang memanggil metode virtual kedua di objek yang mengimplementasikan algoritma konversi.

Tapi itu menyiratkan bahwa objek yang ingin Anda terapkan algoritma perlu berkolaborasi dengan ini: ia perlu memiliki dukungan untuk pola pengunjung yang dipanggang.

Anda tampaknya menggunakan kelas .NET's Windows Forms, yang tidak memiliki dukungan untuk pola pengunjung. Lebih khusus lagi, mereka perlu memiliki public virtual void Accept(IVisitor)metode, yang jelas tidak mereka miliki.

Jadi, apa alternatifnya? Nah, .NET tidak hanya mendukung pengikatan tunggal, juga mendukung pengikatan dinamis, yang bahkan lebih kuat dari pengikatan ganda.

Untuk informasi lebih lanjut tentang cara menerapkan teknik itu, yang akan memungkinkan Anda untuk menyelesaikan masalah Anda (jika saya memahaminya dengan baik), lihat Farewell Visitor .

MEMPERBARUI:

Untuk menerapkan teknik ini ke masalah spesifik Anda, tentukan terlebih dahulu metode ekstensi Anda:

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;
}

Buat operator dinamis:

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

Kemudian isi metode spesifik:

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
}
Kris Vandermotten
sumber
pengiriman dinamis dalam. NET memang cukup kuat .. namun saya perhatikan itu bisa sedikit ... well .. lambat, tetapi tidak dalam satu baris kode yang mengambil garis sevral di beberapa kelas dan antarmuka dengan pengunjung
Newtopian
Namun, pengiriman dinamis tidak akan menyelesaikan masalah saya karena algoritma ToXml saya mengharuskan saya untuk 'mengunjungi' semua jenis rantai pewarisan. Dalam contoh saya, perlu mengunjungi Control, ContainterControl, dan Formulir agar dapat memiliki konversi XML yang sukses.
Nezreli
@Nezreli Ini bisa menyelesaikan masalah Anda, saya sudah memperbarui jawaban saya untuk menunjukkan caranya.
Kris Vandermotten
Saya mengambil izin untuk menambahkan komentar ke definisi variabel dinamis. Butuh dua pembacaan kode sebelum saya melihatnya, dan itu adalah kunci untuk keseluruhan cerita.
Cristi Diaconescu