Sobes.tech
Назад к вопросам
Junior — Middle
57

Можете объяснить концепцию паттерна «посетитель» и его применение в проектировании программ?

Компании, где спрашивали
AstonAston

Ответ от нейросети

sobes.tech AI

Что хотят услышать интервьюеры:

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

Определение:

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

Пример использования:

Допустим, есть иерархия фигур: Circle, Rectangle, Triangle. Нужно уметь считать площадь, экспортировать в JSON и выводить описание. Вместо того чтобы добавлять эти методы в каждую фигуру, можно вынести каждую операцию в отдельного посетителя.

using System;
using System.Collections.Generic;

interface IShape
{
    void Accept(IShapeVisitor visitor);
}

interface IShapeVisitor
{
    void Visit(Circle circle);
    void Visit(Rectangle rectangle);
}

class Circle : IShape
{
    public double Radius { get; }

    public Circle(double radius) => Radius = radius;

    public void Accept(IShapeVisitor visitor) => visitor.Visit(this);
}

class Rectangle : IShape
{
    public double Width { get; }
    public double Height { get; }

    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }

    public void Accept(IShapeVisitor visitor) => visitor.Visit(this);
}

class AreaVisitor : IShapeVisitor
{
    public void Visit(Circle circle)
    {
        Console.WriteLine($"Площадь круга: {Math.PI * circle.Radius * circle.Radius:F2}");
    }

    public void Visit(Rectangle rectangle)
    {
        Console.WriteLine($"Площадь прямоугольника: {rectangle.Width * rectangle.Height:F2}");
    }
}

class Program
{
    static void Main()
    {
        List<IShape> shapes = new()
        {
            new Circle(3),
            new Rectangle(4, 5)
        };

        var areaVisitor = new AreaVisitor();

        foreach (var shape in shapes)
            shape.Accept(areaVisitor);
    }
}

Пояснение кода:

Код показывает классическую схему Visitor. У фигур есть общий контракт IShape с методом Accept, который принимает посетителя. Внутри Accept объект передаёт себя в нужный метод Visit(this), и за счёт этого достигается двойная диспетчеризация: сначала выбирается конкретный тип фигуры, потом — соответствующий метод посетителя.

Шаги работы такие:

  1. Клиент хранит коллекцию объектов через общий интерфейс IShape.
  2. Для выполнения операции создаётся конкретный посетитель, например AreaVisitor.
  3. У каждого объекта вызывается Accept(visitor).
  4. Объект вызывает visitor.Visit(this).
  5. Внутри Visit выполняется логика, зависящая от конкретного типа объекта.

Такой подход удобен, когда нужно добавлять новые операции, не трогая сами классы фигур. Но если в модель часто добавляются новые типы объектов, то приходится расширять интерфейс посетителя и все его реализации.

Ключевые моменты:

  • Visitor отделяет поведение от структуры объектов.
  • Хорошо подходит для стабильных иерархий, где часто добавляются новые операции.
  • Добавление нового типа элемента обычно требует изменения интерфейса посетителя и всех реализаций.
  • Паттерн удобен, когда над объектами нужно выполнять разные независимые действия.
  • Основа паттерна — метод Accept у элемента и методы Visit у посетителя.
  • В C# его часто используют для обхода AST, UI-деревьев, графов объектов и доменных моделей.