Для чого використовується патерн Visitor?
Патерн Visitor (Відвідувач) використовується, коли потрібно виконувати різні операції над об'єктами складної структури, але не змінюючи класи цих об'єктів.
Він дозволяє винести поведінку (операції) за межі ієрархії класів, і додавати нові дії без зміни самих елементів структури.
1. Основна ідея
Розділити структуру даних і поведінку, що застосовується до неї.
Структура залишається незмінною, а нові операції додаються через "відвідувачів", які обходять елементи і виконують над ними потрібну дію.
2. Учасники патерна
| Роль | Опис |
|---|---|
| Visitor (Відвідувач) | Інтерфейс, що визначає набір операцій для різних типів елементів |
| ConcreteVisitor | Конкретні реалізації дій (наприклад, експорт, підрахунок, перевірка) |
| Element (Елемент) | Інтерфейс, який приймає відвідувача (accept(Visitor v)) |
| ConcreteElement | Реальні елементи структури, які "приймають" відвідувача |
| ObjectStructure | Колекція елементів, яку відвідувач може обійти |
3. Приклад (Java)
Уявімо, у нас є структура документів різних типів, і ми хочемо виконувати над ними різні операції (наприклад, підрахунок символів, експорт у PDF тощо), але не хочемо змінювати їхній код щоразу.
Інтерфейси
interface DocumentVisitor {
void visit(TextDocument doc);
void visit(ImageDocument doc);
}
interface DocumentElement {
void accept(DocumentVisitor visitor);
}Елементи
class TextDocument implements DocumentElement {
String text = "Hello Visitor!";
public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
}
class ImageDocument implements DocumentElement {
String name = "image.png";
public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
}Конкретні відвідувачі
class WordCountVisitor implements DocumentVisitor {
public void visit(TextDocument doc) {
System.out.println("Слів: " + doc.text.split(" ").length);
}
public void visit(ImageDocument doc) {
System.out.println("Для зображення підрахунок слів не застосовний");
}
}
class ExportVisitor implements DocumentVisitor {
public void visit(TextDocument doc) {
System.out.println("Експорт тексту в PDF: " + doc.text);
}
public void visit(ImageDocument doc) {
System.out.println("Експорт зображення в PDF: " + doc.name);
}
}Використання
List<DocumentElement> docs = List.of(new TextDocument(), new ImageDocument());
DocumentVisitor visitor = new ExportVisitor();
for (DocumentElement d : docs) {
d.accept(visitor);
}4. Що це дає
- Елементи (структура даних) не змінюються, а відвідувачі реалізують різні сценарії обробки.
- Можна легко додавати нові дії, просто створивши новий
Visitor. - Підтримується подвійна диспетчеризація: поведінка залежить і від типу відвідувача, і від типу елемента.
5. Переваги
Додавання нових операцій без зміни класів елементів. Централізація логіки обробки кожного типу. Розділення даних і дій - код стає чистішим.
6. Недоліки
- Додавання нових типів елементів вимагає зміни всіх відвідувачів.
- Збільшується кількість класів.
- Використовує подвійну диспетчеризацію, що ускладнює архітектуру.
7. Коли використовувати
- Коли потрібно часто додавати нові операції, але структура даних рідко змінюється.
- Коли об'єкти різних класів повинні оброблятися єдиним способом.
- Приклад: AST-парсери, компілятори, експорт даних, звітні системи.
Висновок
Патерн Visitor використовується, щоб:
- розділити структуру і операції над нею,
- додавати нові дії без зміни класів елементів,
- забезпечити обхід і обробку різних типів об'єктів єдиною логікою.
Підсумок:
Visitor - це "гість", який приходить в об'єкти, робить з ними потрібну дію, але не змінює їх зсередини.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.