Як Visitor допомагає додавати нові операції без зміни класів?
Патерн Visitor (Відвідувач) дозволяє додавати нові операції над об'єктами без зміни їхніх класів, тому що вся логіка нових дій виноситься у зовнішній об'єкт - відвідувача.
1. Проблема без Visitor
Припустімо, є декілька типів елементів:
class Circle { void draw() { ... } }
class Square { void draw() { ... } }Тепер потрібно додати нові операції:
- експорт у PDF,
- розрахунок площі,
- валідація параметрів.
Без Visitor довелося б у кожен клас додавати нові методи:
class Circle {
void draw() { ... }
void exportToPDF() { ... } // додали
void calculateArea() { ... } // додали
}Проблеми:
- щоразу доводиться редагувати наявні класи;
- при десятках операцій і елементів код швидко розростається;
- порушується принцип Open/Closed (класи відкриті для модифікацій).
2. Як це вирішує Visitor
Visitor виносить усі "дії" з самих класів у зовнішні об'єкти - відвідувачів. Тепер кожен новий алгоритм створюється у вигляді нового класу, а не як нові методи всередині елементів.
3. Структура
interface Shape {
void accept(ShapeVisitor visitor);
}
interface ShapeVisitor {
void visit(Circle c);
void visit(Square s);
}
class Circle implements Shape {
public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}
class Square implements Shape {
public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}4. Додавання нових операцій
Щоб додати, наприклад, експорт у PDF, потрібно просто створити нового відвідувача:
class ExportToPDFVisitor implements ShapeVisitor {
public void visit(Circle c) {
System.out.println("Експорт кола в PDF");
}
public void visit(Square s) {
System.out.println("Експорт квадрата в PDF");
}
}Для іншої операції (наприклад, підрахунок площі) - інший відвідувач:
class AreaCalculationVisitor implements ShapeVisitor {
public void visit(Circle c) { System.out.println("Обчислення площі кола"); }
public void visit(Square s) { System.out.println("Обчислення площі квадрата"); }
}Елементи (Circle, Square) залишаються незмінними - вони просто "приймають" відвідувача.
5. Використання
List<Shape> shapes = List.of(new Circle(), new Square());
// Додаємо будь-яку нову операцію без зміни Shape-класів
ShapeVisitor exportVisitor = new ExportToPDFVisitor();
ShapeVisitor areaVisitor = new AreaCalculationVisitor();
for (Shape shape : shapes) {
shape.accept(exportVisitor);
shape.accept(areaVisitor);
}6. Чому це працює
- Елементи знають лише, як прийняти відвідувача (
accept()), але не знають, що він з ними робить. - Відвідувач реалізує всі конкретні операції для кожного типу елемента.
- Додавання нової операції = новий
Visitor, без торканняShape,Circle,Square.
7. Принцип, що реалізується
Visitor втілює принцип Open/Closed:
Класи елементів закриті для модифікації, але відкриті для розширення через нових відвідувачів.
Висновок
Visitor дозволяє додавати нові операції, не змінюючи класи самих об'єктів, тому що:
- Уся логіка нових дій винесена в зовнішні класи (відвідувачів).
- Елементи лише надають точку входу - метод
accept(). - Додавання нової поведінки = створення нового відвідувача, а не зміна наявних класів.
Підсумок:
Visitor переносить зміни зсередини класів у зовнішній шар поведінки, зберігаючи стабільність структури і гнучкість системи.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.