Посетитель

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

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

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

  • Когда нужно соблюсти принцип открытости/закрытости: добавлять новое поведение, не модифицируя классы.
Основные участники паттерна
Visitor (Посетитель) — интерфейс, описывающий операцию, которую выполняют над каждым типом объекта.

ConcreteVisitor (Конкретный посетитель) — реализация интерфейса Visitor, содержащая конкретные операции для каждого типа объектов.

Element (Элемент) — интерфейс объекта, над которым выполняются операции. Он объявляет метод accept, принимающий объект Visitor.

ConcreteElement (Конкретный элемент) — реализация элемента, которая вызывает конкретные методы посетителя.
Пример реализации Visitor
Давайте представим структуру, состоящую из разных типов документов: PDFDocument и WordDocument. Мы хотим выполнить над ними разные операции, такие как print и export. Добавляя новый тип операции, будем использовать «Посетителя» для добавления функционала без изменения самих классов документов.
Шаг 1: Определим классы элементов (документы)
from abc import ABC, abstractmethod

class Document(ABC):
    @abstractmethod
    def accept(self, visitor):
        pass

class PDFDocument(Document):
    def accept(self, visitor):
        visitor.visit_pdf_document(self)

    def get_pdf_text(self):
        return "This is a PDF document text."

class WordDocument(Document):
    def accept(self, visitor):
        visitor.visit_word_document(self)

    def get_word_text(self):
        return "This is a Word document text."
В этом коде:
  • Document — это абстрактный базовый класс с методом accept.
  • PDFDocument и WordDocument — конкретные классы документов, реализующие метод accept, который принимает объект Visitor
Шаг 2: Определим интерфейс Visitor и его реализацию
class Visitor(ABC):
    @abstractmethod
    def visit_pdf_document(self, document: PDFDocument):
        pass

    @abstractmethod
    def visit_word_document(self, document: WordDocument):
        pass

class PrintVisitor(Visitor):
    def visit_pdf_document(self, document: PDFDocument):
        print("Printing PDF:", document.get_pdf_text())

    def visit_word_document(self, document: WordDocument):
        print("Printing Word Document:", document.get_word_text())

class ExportVisitor(Visitor):
    def visit_pdf_document(self, document: PDFDocument):
        print("Exporting PDF to text:", document.get_pdf_text())

    def visit_word_document(self, document: WordDocument):
        print("Exporting Word Document to text:", document.get_word_text())
В этом коде:
  • Visitor — интерфейс, определяющий методы visit_pdf_document и visit_word_document для каждого типа документа.
  • PrintVisitor и ExportVisitor — конкретные реализации Visitor, выполняющие операции print и export.
Шаг 3: Использование паттерна «Посетитель»
# Создаем документы
pdf = PDFDocument()
word = WordDocument()

# Создаем посетителей
print_visitor = PrintVisitor()
export_visitor = ExportVisitor()

# Выполняем операции через посетителей
pdf.accept(print_visitor)  # Printing PDF: This is a PDF document text.
pdf.accept(export_visitor)  # Exporting PDF to text: This is a PDF document text.

word.accept(print_visitor)  # Printing Word Document: This is a Word document text.
word.accept(export_visitor)  # Exporting Word Document to text: This is a Word document text.
Как работает код?
  • Каждый документ вызывает accept, передавая объект посетителя.
  • В методе accept соответствующего документа вызывается метод visit, который соответствует типу документа (visit_pdf_document для PDF и visit_word_document для Word).
  • Это позволяет легко добавлять новые операции, как например, создание новой реализации Visitor без изменений классов PDFDocument и WordDocument.
Преимущества
  • Простое добавление новых операций. При добавлении новой операции не нужно менять классы объектов, достаточно создать новый класс Visitor.

  • Соблюдение принципа открытости/закрытости. Можно добавлять поведение для объектов, не изменяя их.

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

  • Сложность для вложенных структур. Реализация паттерна «Посетитель» может стать сложной, если элементы имеют сложные иерархии или вложенные элементы.