Что такое паттерн проектирования Proxy?

«Что такое паттерн проектирования Proxy?» — вопрос из категории Паттерны, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн Proxy (Заместитель) — это структурный паттерн, предоставляющий объект-заменитель или placeholder для другого объекта. Прокси контролирует доступ к оригинальному объекту, позволяя выполнять дополнительную логику до или после основного вызова.

Основные сценарии использования:

  • Ленивая инициализация (Virtual Proxy): Отложить создание ресурсоёмкого объекта до момента реальной необходимости.
  • Контроль доступа (Protection Proxy): Проверить права доступа перед вызовом методов реального объекта.
  • Кеширование (Cache Proxy): Сохранять результаты дорогостоящих операций и возвращать кешированные данные.
  • Логирование и мониторинг: Записывать вызовы методов, время выполнения.
  • Удалённый доступ (Remote Proxy): Представлять объект, находящийся в другом адресном пространстве (например, при использовании удалённых сервисов).

Типы Proxy:

  • Virtual Proxy – откладывает создание ресурсоемкого объекта.
  • Protection Proxy – контролирует доступ к объекту.
  • Remote Proxy – представляет объект в другом адресном пространстве.
  • Smart Proxy – добавляет дополнительную логику (кеширование, логирование).

Пример (Virtual Proxy на C#):

// Интерфейс, общий для реального объекта и прокси
public interface IImage
{
    void Display();
}

// Реальный, «тяжёлый» объект
public class RealImage : IImage
{
    private string _fileName;
    public RealImage(string fileName)
    {
        _fileName = fileName;
        LoadFromDisk(); // Дорогая операция
    }
    private void LoadFromDisk() => Console.WriteLine($"Loading image '{_fileName}' from disk...");
    public void Display() => Console.WriteLine($"Displaying image '{_fileName}'");
}

// Прокси-объект
public class ProxyImage : IImage
{
    private RealImage _realImage;
    private string _fileName;

    public ProxyImage(string fileName) => _fileName = fileName;

    public void Display()
    {
        // Создаём реальный объект только при первом вызове
        if (_realImage == null)
        {
            _realImage = new RealImage(_fileName);
        }
        _realImage.Display();
    }
}

// Использование
var image = new ProxyImage("high_res_photo.jpg");
// RealImage ещё не создан
image.Display(); // Вывод: "Loading image 'high_res_photo.jpg' from disk..." затем "Displaying image 'high_res_photo.jpg'"
image.Display(); // Вывод: "Displaying image 'high_res_photo.jpg'" (объект уже загружен)

Практическое применение: В .NET паттерн Proxy лежит в основе многих технологий, таких как:

  • Entity Framework Core (ленивая загрузка навигационных свойств).
  • МОК-объекты в юнит-тестах (например, в Moq или NSubstitute).
  • WCF/Remoting клиенты для удалённых сервисов.