← Назад к вопросам
В чем разница между шаблонами проектирования 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. Таблица сравнения
| Аспект | Proxy | Decorator |
|---|---|---|
| Цель | Контролировать доступ | Добавить функциональность |
| Инициализация объекта | Может отложить | Требует готовый объект |
| Количество слоёв | Обычно 1 | Может быть много |
| Изменение интерфейса | Может отличаться | Совпадает с оригиналом |
| Отношение к объекту | Контролирует | Расширяет |
| Пример | Lazy loading, кэширование | Добавление логирования, шифрование |
7. Когда использовать
Proxy
- Отложенная инициализация (lazy loading)
- Контроль доступа (access control)
- Логирование и аудит доступа
- Кэширование результатов
- Remote proxy (для удалённых объектов)
Decorator
- Добавление функциональности на лету
- Комбинирование поведений
- Альтернатива наследованию
- Обёртывание для различных версий
Вывод
Хотя оба паттерна используют обёртывание (wrapping), они решают разные проблемы:
- Proxy контролирует доступ к объекту
- Decorator расширяет функциональность объекта
Выбор зависит от того, что вы хотите сделать: ограничить доступ (Proxy) или добавить функциональность (Decorator).