Ответ
Паттерн «Посетитель» — это поведенческий паттерн, который позволяет добавлять новые операции к объектам, не изменяя их классы. Он полезен, когда у вас есть сложная структура объектов (например, дерево AST — абстрактного синтаксического дерева) и вы хотите выполнять над ними разнородные операции (логирование, рендеринг, анализ).
Ключевая идея: Вы отделяете алгоритм от структуры объектов, на которых он работает. Новые операции добавляются путём создания нового класса «посетителя».
Основные компоненты:
IVisitor— интерфейс с методамиVisitдля каждого типа элемента в структуре.IElement— интерфейс элемента, который принимает посетителя (методAccept).- Конкретные элементы (Concrete Elements) — реализуют
Accept, вызывая соответствующий метод посетителя. - Конкретные посетители (Concrete Visitors) — реализуют интерфейс
IVisitor, определяя поведение для каждого типа элемента.
Пример на C#:
// Элементы
public interface IShape
{
void Accept(IShapeVisitor visitor);
}
public class Circle : IShape
{
public double Radius { get; set; }
public void Accept(IShapeVisitor visitor) => visitor.Visit(this);
}
public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public void Accept(IShapeVisitor visitor) => visitor.Visit(this);
}
// Посетитель
public interface IShapeVisitor
{
void Visit(Circle circle);
void Visit(Rectangle rectangle);
}
// Конкретная операция — вычисление площади
public class AreaCalculator : IShapeVisitor
{
public double TotalArea { get; private set; }
public void Visit(Circle circle)
{
TotalArea += Math.PI * circle.Radius * circle.Radius;
}
public void Visit(Rectangle rectangle)
{
TotalArea += rectangle.Width * rectangle.Height;
}
}
// Использование
List<IShape> shapes = new List<IShape> { new Circle { Radius = 5 }, new Rectangle { Width = 2, Height = 3 } };
var calculator = new AreaCalculator();
foreach (var shape in shapes)
{
shape.Accept(calculator);
}
Console.WriteLine($"Total area: {calculator.TotalArea}");
Преимущества:
- Открыт/закрыт: Легко добавить новую операцию (нового посетителя), не трогая классы элементов.
- Сбор родственных операций: Логика одной операции сосредоточена в одном классе посетителя.
Недостатки:
- Сложно добавить новый тип элемента, так как придётся обновить всех существующих посетителей.
- Посетитель может получить доступ к приватным полям элементов только если они ему «откроются» (например, через внутренний интерфейс).