Что такое паттерн "Посетитель" (Visitor)

Посетитель — поведенческий паттерн проектирования, который позволяет добавлять новые операции к объектам без изменения их классов. Для этого используется отдельный объект — посетитель, который реализует нужные операции, а объекты принимают посетителя и вызывают соответствующие методы.

Представьте структуру объектов — например, элементы документа: текст, изображения, таблицы. Чтобы выполнить разные операции (например, подсчёт статистики, экспорт или рендеринг), можно создать разные посетители, не меняя классы элементов.

Преимущества паттерна «Посетитель»

  • Позволяет добавлять новые операции без изменения существующих классов.
  • Разделяет алгоритмы и структуру объектов.
  • Упрощает поддержку и расширение функционала.

Пример:

  • Интерфейс посетителя с методами для каждого типа элемента.
  • Элементы с методом accept(visitor), который вызывает метод посетителя.

Где применяется паттерн «Посетитель»?

  • Компиляторы и парсеры.
  • Обработка сложных структур данных (AST, DOM).
  • Системы, где нужно выполнять разнообразные операции над объектами без изменения их классов.

Пример реализации на JavaScript

// Интерфейс посетителя class Visitor { visitText(element) { throw new Error("Метод visitText() должен быть реализован"); } visitImage(element) { throw new Error("Метод visitImage() должен быть реализован"); } } // Элементы с методом accept class TextElement { constructor(text) { this.text = text; } accept(visitor) { visitor.visitText(this); } } class ImageElement { constructor(url) { this.url = url; } accept(visitor) { visitor.visitImage(this); } } // Конкретный посетитель: подсчёт символов и изображений class CountVisitor extends Visitor { constructor() { super(); this.textCount = 0; this.imageCount = 0; } visitText(element) { this.textCount += element.text.length; } visitImage(element) { this.imageCount += 1; } } // Использование const elements = [ new TextElement("Привет"), new ImageElement("image1.png"), new TextElement("Мир"), ]; const visitor = new CountVisitor(); elements.forEach((el) => el.accept(visitor)); console.log(`Всего символов: ${visitor.textCount}`); console.log(`Всего изображений: ${visitor.imageCount}`);

Когда стоит использовать паттерн «Посетитель»?

  • Когда нужно добавить новые операции, не изменяя классы объектов.
  • При работе с сложными иерархиями объектов.
  • Когда операции над объектами часто меняются, а классы — нет.

Отличие от других паттернов

  • В отличие от Команды, где объект инкапсулирует действие, здесь посетитель инкапсулирует операцию над структурой.
  • Отличается от Декоратора, который изменяет поведение объекта, а Посетитель добавляет новые операции.

Плюсы паттерна

  • Позволяет легко расширять функциональность.
  • Централизует операции над объектами.
  • Упрощает добавление новых операций.

Минусы паттерна

  • Сложно добавить новые типы элементов — нужно менять интерфейс посетителя и все его реализации.
  • Может привести к усложнению кода из-за множества классов.