← Назад к вопросам

В чем разница между шаблонами проектирования Proxy и Decorator?

2.0 Middle🔥 141 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерны Proxy и Decorator: различия и сходства

В то время как Proxy и Decorator выглядят похоже (оба оборачивают объект), они решают совершенно разные проблемы и имеют разные цели.

1. Основная цель

Proxy (Заместитель)

  • Контролирует доступ к оригинальному объекту
  • Решает проблему взаимодействия между клиентом и объектом
  • Может отложить создание объекта или контролировать его использование
// Proxy контролирует доступ
public class AccessControlProxy implements DataService {
    private DataService realService;
    private User currentUser;
    
    public AccessControlProxy(DataService realService, User user) {
        this.realService = realService;
        this.currentUser = user;
    }
    
    @Override
    public void deleteData(String id) {
        if (!currentUser.hasPermission("DELETE")) {
            throw new AccessDeniedException("Нет прав на удаление");
        }
        realService.deleteData(id);  // Только если разрешено
    }
}

Decorator (Декоратор)

  • Добавляет функциональность к объекту
  • Решает проблему расширения функциональности
  • Может комбинироваться множественно
// Decorator добавляет функциональность
public class LoggingDecorator implements DataService {
    private DataService wrappedService;
    
    public LoggingDecorator(DataService service) {
        this.wrappedService = service;
    }
    
    @Override
    public void deleteData(String id) {
        System.out.println("[LOG] Удаляю данные с id: " + id);
        wrappedService.deleteData(id);
        System.out.println("[LOG] Данные удалены");
    }
}

2. Структура классов

Proxy

// Proxy часто создаёт реальный объект
public class ExpensiveObjectProxy implements ExpensiveObject {
    private RealExpensiveObject realObject;  // Может быть null
    
    @Override
    public void doSomething() {
        if (realObject == null) {
            realObject = new RealExpensiveObject();  // Ленивая инициализация
        }
        realObject.doSomething();
    }
}

Decorator

// Decorator получает объект как параметр
public class BufferedDecorator implements DataService {
    private final DataService service;  // Обязательный, не null
    private final List<String> buffer;
    
    public BufferedDecorator(DataService service) {
        this.service = service;
        this.buffer = new ArrayList<>();
    }
}

3. Количество слоёв

Proxy - обычно один слой:

DataService service = new RealDataService();
DataService proxy = new AccessControlProxy(service, user);
// Обычно не добавляем ещё один прокси

Decorator - можно складывать:

DataService service = new RealDataService();
DataService logged = new LoggingDecorator(service);
DataService buffered = new BufferedDecorator(logged);
DataService cached = new CachingDecorator(buffered);
// Можем комбинировать столько, сколько нужно

4. Отношение к интерфейсу

Proxy - может отличаться от интерфейса оригинального объекта:

public class SmartProxy implements DataService {
    private DataService realService;
    
    // Может добавить новые методы
    public void clearCache() {
        System.out.println("Кэш очищен");
    }
}

Decorator - полностью соответствует интерфейсу:

public abstract class DataServiceDecorator implements DataService {
    protected DataService wrappedService;
    
    // Только методы интерфейса DataService
    @Override
    public abstract String getData(String id);
}

5. Практические примеры

Proxy - Lazy Loading (Ленивая загрузка)

public interface Image {
    void display();
}

public class RealImage implements Image {
    private String filename;
    
    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();  // Дорогая операция
    }
    
    private void loadImageFromDisk() {
        System.out.println("Загружаю: " + filename);
    }
    
    @Override
    public void display() {
        System.out.println("Показываю: " + filename);
    }
}

// Proxy откладывает загрузку
public class ImageProxy implements Image {
    private RealImage realImage;
    private String filename;
    
    public ImageProxy(String filename) {
        this.filename = filename;  // Не загружаем ещё
    }
    
    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);  // Загружаем только при необходимости
        }
        realImage.display();
    }
}

// Использование
Image image = new ImageProxy("photo.jpg");
image.display();  // Здесь произойдёт загрузка

Proxy - Кэширование (Remote Proxy)

public interface WeatherService {
    String getWeather(String city);
}

public class RemoteWeatherService implements WeatherService {
    @Override
    public String getWeather(String city) {
        // HTTP запрос на удалённый сервер
        System.out.println("HTTP запрос к weather-api.com");
        return "Sunny";
    }
}

public class CachingWeatherProxy implements WeatherService {
    private RemoteWeatherService realService;
    private Map<String, String> cache = new HashMap<>();
    
    public CachingWeatherProxy(RemoteWeatherService service) {
        this.realService = service;
    }
    
    @Override
    public String getWeather(String city) {
        if (cache.containsKey(city)) {
            System.out.println("Из кэша");
            return cache.get(city);
        }
        
        String weather = realService.getWeather(city);
        cache.put(city, weather);
        return weather;
    }
}

Decorator - Добавление функциональности

public interface TextFormatter {
    String format(String text);
}

public class PlainTextFormatter implements TextFormatter {
    @Override
    public String format(String text) {
        return text;
    }
}

// Декоратор для добавления заглавных букв
public class UppercaseDecorator implements TextFormatter {
    private TextFormatter wrappedFormatter;
    
    public UppercaseDecorator(TextFormatter formatter) {
        this.wrappedFormatter = formatter;
    }
    
    @Override
    public String format(String text) {
        return wrappedFormatter.format(text).toUpperCase();
    }
}

// Декоратор для добавления кавычек
public class QuoteDecorator implements TextFormatter {
    private TextFormatter wrappedFormatter;
    
    public QuoteDecorator(TextFormatter formatter) {
        this.wrappedFormatter = formatter;
    }
    
    @Override
    public String format(String text) {
        return "\"" + wrappedFormatter.format(text) + "\"";
    }
}

// Использование
TextFormatter formatter = new PlainTextFormatter();
formatter = new UppercaseDecorator(formatter);
formatter = new QuoteDecorator(formatter);

System.out.println(formatter.format("hello"));  // "HELLO"

Decorator - Java Collections пример

// Collections.synchronizedList() - это Decorator!
List<String> unsafeList = new ArrayList<>();
List<String> safeList = Collections.synchronizedList(unsafeList);

// Collections.unmodifiableList() - тоже Decorator!
List<String> mutableList = new ArrayList<>();
List<String> immutableList = Collections.unmodifiableList(mutableList);

6. Таблица сравнения

АспектProxyDecorator
ЦельКонтролировать доступДобавить функциональность
Инициализация объектаМожет отложитьТребует готовый объект
Количество слоёвОбычно 1Может быть много
Изменение интерфейсаМожет отличатьсяСовпадает с оригиналом
Отношение к объектуКонтролируетРасширяет
ПримерLazy loading, кэшированиеДобавление логирования, шифрование

7. Когда использовать

Proxy

  • Отложенная инициализация (lazy loading)
  • Контроль доступа (access control)
  • Логирование и аудит доступа
  • Кэширование результатов
  • Remote proxy (для удалённых объектов)

Decorator

  • Добавление функциональности на лету
  • Комбинирование поведений
  • Альтернатива наследованию
  • Обёртывание для различных версий

Вывод

Хотя оба паттерна используют обёртывание (wrapping), они решают разные проблемы:

  • Proxy контролирует доступ к объекту
  • Decorator расширяет функциональность объекта

Выбор зависит от того, что вы хотите сделать: ограничить доступ (Proxy) или добавить функциональность (Decorator).

В чем разница между шаблонами проектирования Proxy и Decorator? | PrepBro