Розуміння шаблону відвідувачів


16

У мене є ієрархія класів, яка представляє елементи управління графічним інтерфейсом. Щось на зразок цього:

Control->ContainerControl->Form

Мені доводиться реалізовувати низку алгоритмів, які працюють з об'єктами, які роблять різні речі, і я думаю, що шаблон відвідувачів був би найчистішим рішенням. Візьмемо для прикладу алгоритм, який створює представлення 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?

Причина, по якій я розглядаю шаблон відвідувачів, полягає в тому, що для деяких алгоритмів потрібні посилання, недоступні в проекті, де реалізовані класи, і існує ряд різних алгоритмів, тому я уникаю великих класів.


яке Ваше запитання?
гнат

В основному, як переписати метод ToXml (), використовуючи шаблон відвідувача.
Незрелі


Дякуємо за посилання Динамічна відправка спрощує традиційну схему відвідувачів, але вона не сильно змінюється.
Незрелі

@Nezreli Так. Він працює з класами, які не підтримують шаблон відвідувачів, наприклад, з керуючими елементами Windows Forms, з якими ви маєте справу.
Кріс Вандермоттен

Відповіді:


17

Шаблон відвідувачів - це механізм моделювання подвійного прив'язки в мовах програмування, що підтримують лише одне прив'язування. На жаль, це твердження може не дуже прояснити речі, тому дозвольте пояснити на простому прикладі.

У .NET і C #, платформі, яку ви використовуєте, об'єкти можна перетворити на рядки за допомогою ToString()функції. Те, що робить ця функція, тобто код, який виконується, залежить від типу об'єкта, до якого ви його застосовуєте (це віртуальний метод). Який код виконується, залежить від однієї речі, одного типу об'єкта, отже, використовуваний механізм називається одинарним зв'язуванням.

Але що робити, якщо я хочу мати декілька способів перетворення об'єкта в рядок для кожного різного виду об'єктів? Що робити, якщо я хотів мати два способи перетворення об’єктів у рядки, так що код, який виконується, залежить від двох речей: не тільки об'єкта, який потрібно перетворити, але і способу, яким ми хочемо його перетворити?

Це можна було б добре вирішити, якби ми мали подвійне зв’язування. Але більшість мов OO, включаючи C #, підтримують лише одне прив'язування.

Шаблон відвідувача вирішує проблему, перетворюючи подвійне зв'язування на два послідовних одиничних прив'язки.

У нашому прикладі вище, він використовував би віртуальний метод в об'єкті для перетворення, який викликає другий віртуальний метод в об'єкті, що реалізує алгоритм перетворення.

Але це означає, що об’єкт, до якого потрібно застосувати алгоритм, повинен співпрацювати з цим: він повинен мати підтримку для шаблону відвідувачів, запечений у ньому.

Здається, ви користуєтеся класами Windows Forms .NET, які не підтримують шаблон відвідувачів. Більш конкретно, їм потрібно мати public virtual void Accept(IVisitor)метод, якого, очевидно, немає.

Отже, яка альтернатива? Що ж.

Для отримання додаткової інформації про те, як застосувати цю техніку, яка дозволить вам вирішити свою проблему (якщо я це добре розумію), подивіться на Прощальний відвідувач .

ОНОВЛЕННЯ:

Щоб застосувати техніку до вашої конкретної проблеми, спочатку визначте метод розширення:

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 насправді досить потужна .. однак я помітив, що це може бути трохи ... ну .. повільно, але він робить в одному рядку коду те, що займає кілька рядків у декількох класах та інтерфейси з відвідувачем
ньютопський

Тим не менш, динамічна відправка не вирішить мою проблему, оскільки мій алгоритм ToXml вимагає від мене "відвідування" всіх типів по ланцюжку спадкування. У моєму прикладі для того, щоб пройти успішне перетворення XML, потрібно відвідати Control, ContainterControl та Form.
Незрелі

@Nezreli Це може вирішити вашу проблему, я оновив свою відповідь, щоб показати вам, як.
Кріс Вандермоттен

Я взяв дозвіл на додавання коментаря до визначення динамічної змінної. Перш ніж я помітив це, мені знадобилося два читання коду, і це ключ до всієї історії.
Крісті Діаконеску
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.