Відвідувач (шаблон проектування)

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до: навігація, пошук

Відвідувач (Visitor) — шаблон проектування, який дозволяє відділити певний алгоритм від елементів, на яких алгоритм має бути виконаний, таким чином можливо легко додати або ж змінити алгоритм без змін щодо елементів системи. Практичним результатом є можливість додавання нових операцій в існуючі структури об'єкта без зміни цих структур.

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

Проблема, яку вирішує[ред.ред. код]

Над кожним об'єктом деякої структури виконується одна або більше операцій. Визначити нову операцію, не змінюючи класи об'єктів.

Випадки застосування[ред.ред. код]

Шаблон Відвідувач використовується коли:

  • існує багато об'єктів різних класів з різними інтерфейсами, і потрібно виконати операцію над цими об'єктами, залежно від їх типу.
  • потрібно виконувати над об'єктами багато не пов'язаних між собою операцій, але не хочеться "забруднювати" цими операціями класи об'єктів. При цьому, якщо ці об'єкти використовуються у декількох проектах, з'являється можливість включати операції лише в ті проекти, де вони необхідні.
  • класи, які визначають структуру даних, змінюються рідко, але є потреба часто визначати нові операції над цією структурою. Якщо ж класи, які визначають структуру даних, змінюються часто, краще визначити операції в цих класах, адже при зміні інтерфейсів у класі даних необхідно змінювати і класи, які реалізують операції над цією структурою.
  • треба визначити операцію над деякою структурою, не змінюючи клас цієї структури.

Опис шаблону

Основним призначенням шаблону Відвідувач є введення абстрактної функціональності для сукупної ієрархічної структури об'єктів «елемент», а саме, шаблон Відвідувач дозволяє, не змінюючи класи елементів, додавати в них нові операції. Для цього вся обробна функціональність переноситься з самих класів елементів в ієрархію спадкування Відвідувача.

Шаблон відвідувач дозволяє легко додавати нові операції — потрібно просто додати новий похідний від відвідувача клас. Однак, шаблон Відвідувач слід використовувати тільки в тому випадку, якщо підкласи елементів сукупної ієрархічної структури залишаються стабільними (незмінними). В іншому випадку, потрібно докласти значних зусиль на оновлення всієї ієрархії.

Іноді наводяться заперечення з приводу використання шаблону Відвідувач, оскільки він розділяє дані та алгоритми, що суперечить концепції об'єктно-орієнтованого програмування . Однак успішний досвід застосування STL, де поділ даних і алгоритмів покладено в основу, доводить можливість використання Відвідувача.

Реалізація[ред.ред. код]

Особливості шаблону[ред.ред. код]

  • Сукупна структура об'єктів елементу може визначатися за допомогою патерну Компонувальник (Composite).
  • Для обходу може використовуватися Ітератор (Iterator).
  • Шаблон Відвідувач демонструє класичний прийом відновлення інформації про втрачені типи, не вдаючись до понижуючого приведення типів(динамічне приведення).

Частини шаблону[ред.ред. код]

VisitorDiagram.svg
  • Відвідувач (зазвичай, абстрактний клас чи інтерфейс)
    • визначає дію над кожним класом конкретних елементів. Ім'я та сигнатура операції мають визначати конкретний клас даних, елемент якого треба відвідати. Це дає можливість відвідувачу доступатися до елементів через інтерфейс конкретного класу.
  • Конкретний відвідувач (конкретний клас, що наслідує Відвідувач)
    • реалізує чи перевизначає операції, визначені в базовому класі. Містить алгоритми, які виконуватимуться над об'єктами відповідного класу. Також даний клас утримує локальний стан алгоритмів (цей стан, зазвичай, утримує проміжні результати під час обходу структури, та ін.).
  • Елемент (зазвичай, абстрактний клас чи інтерфейс)
    • визначає операцію, яка приймає об'єкт відвідувача як аргумент.
  • Конкретний елемент (конкретний клас, що наслідує Елемент)
    • визначає операцію, що приймає об'єкт відвідувача як аргумент.
  • Структура елементів (клас, що реалізує структуру елементів)
    • може перераховувати елементи, які містить.
    • надає високорівневий інтерфейс, що дозволяє відвідувачу виконувати елементи.
    • може бути Компонувальником чи колекцією (як список або черга).

Взаємодія[ред.ред. код]

  • Клієнт, що використовує Відвідувача, має створити об'єкт Конкретного відвідувача та обійти потрібні елементи, відвідуючи кожен з них цим об'єктом.
  • Коли елемент відвідується, він викликає операцію Відвідувача, що відповідає класу елементу. Елемент передає себе як аргумент в цю операцію щоб дозволити Відвідувачу доступ до його стану, якщо це необхідно.

Приклади реалізації[ред.ред. код]

С++[ред.ред. код]

class Visitor { // Відвідувач 

    public: 
        virtual void VisitElementA(ElementA*); 
        virtual void VisitElementB(ElementB*);  

    // and so on for other concrete elements 

    protected: 
        Visitor(); 

}; 

class Element { // Елемент 

    public: 
        virtual ~Element(); 
        virtual void Accept(Visitor&) = 0; 

    protected: 
        Element(); 

};  

class ElementA : public Element { // Конкретний елемент А 

    public: 
        ElementA(); 
        virtual void Accept(Visitor& v) { v.VisitElementA(this); } 

};  

class ElementB : public Element { // Конкретний елемент Б 

    public: 
        ElementB(); 
        virtual void Accept(Visitor& v) { v.VisitElementB(this); } 

}; 

class CompositeElement : public Element { // Колекція 

    public: 
        virtual void Accept(Visitor&); 

    private: 
        List<Element*>* _children; 

};  

void CompositeElement::Accept (Visitor& v) { 
    ListIterator<Element*> i(_children); 
    for (i.First(); !i.IsDone(); i.Next()) { 
        i.CurrentItem()->Accept(v); 
    } 
    v.VisitCompositeElement(this); 
} 

С#[ред.ред. код]

class Visitor { // Відвідувач

    public virtual void VisitElementA(ElementA element); 
    public virtual void VisitElementB(ElementB element);  

    // and so on for other concrete elements 
} 

class Element { // Елемент 

    public virtual void Accept(Visitor visitor); 

}  

class ElementA : public Element { // Конкретний елемент А 

    public virtual void Accept(Visitor visitor) { visitor.VisitElementA(this); } 

}  

class ElementB : public Element { // Конкретний елемент Б 

    public virtual void Accept(Visitor visitir) { visitor.VisitElementB(this); } 

} 

class CompositeElement : public Element { // Структура елементів 

    public virtual void Accept(Visitor visitor)  
    { 
        foreach (Element i in children) 
        { 
            i.CurrentItem()->Accept(visitor); 
        } 
        v.VisitCompositeElement(this); 
    } 

    private List<Element> children; 

}  

Див. також[ред.ред. код]

Посилання[ред.ред. код]