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

Ответ

Паттерн 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 клиенты для удалённых сервисов.

Ответ 18+ 🔞

Давай разберем эту проксю, а то звучит как что-то из шпионских фильмов, а на деле — просто хитрая обёртка, чтобы не париться.

Смотри, представь, что у тебя есть объект, который жрёт оперативку, как не в себя, или грузится с диска целую вечность. Создавать его сразу — всё равно что включать весь домофонный щиток, чтобы позвонить в одну квартиру. Идиотизм.

Вот тут и выходит на сцену Прокси (Заместитель). Это такой подставной чувак, который делает вид, что он и есть тот самый тяжелый объект. У него та же самая рожа (интерфейс), но внутри он — просто понторез.

Зачем это, блядь, нужно?

  • Ленивая загрузка (Virtual Proxy): Не создавать объект, пока он реально не понадобится. Зачем грузить гигабайтную картинку, если пользователь может просто пролистать её и не открывать?
  • Защита доступа (Protection Proxy): Как вышибала в клубе. Прежде чем пустить тебя к реальному объекту (методу), проверит твой access level. Не пройдёшь — получишь по ебалу.
  • Кеширование (Cache Proxy): Запоминает результат дорогой операции. Следующий раз, когда спросят, не будет опять лезть в базу или считать десять минут, а тупо выдаст сохранённый ответ. Умно, да?
  • Логирование: Чтобы потом, когда всё наебнётся, можно было посмотреть, кто, когда и какую хуйню вызывал.
  • Удалённый доступ (Remote 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($"Гружу картинку '{_fileName}' с диска... Овердохуища времени...");
    public void Display() => Console.WriteLine($"Показываю картинку '{_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("photo_of_my_vacation_in_4k.jpg");
// На этом этапе RealImage НЕ создан! Прокси — просто пустышка с названием файла.
Console.WriteLine("Прокси создан, все быстро и безболезненно.");

// Первый вызов — будет больно, но только один раз.
image.Display(); // Вывод: "Гружу картинку 'photo_of_my_vacation_in_4k.jpg' с диска..." -> "Показываю картинку..."
// Второй вызов — уже легко, объект-то уже есть!
image.Display(); // Вывод: "Показываю картинку 'photo_of_my_vacation_in_4k.jpg'"

Где это валяется в .NET? Да почти везде, если присмотреться!

  • Entity Framework Core: Твои навигационные свойства (List<Order>) часто — ленивые прокси. Они не грузят заказы из базы, пока ты к ним явно не обратишься.
  • Моки в тестах (Moq, NSubstitute): Это вообще чистейшие прокси-объекты, которые подменяют реальные сервисы и записывают, какие методы ты вызывал.
  • Клиенты WCF: Когда ты вызываешь метод удалённого сервиса, твой локальный клиент — это прокси, который упаковывает вызов, шлёт его по сети и получает ответ.

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