Что такое паттерн прокси?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Что такое паттерн Прокси (Proxy Pattern)
Паттерн Прокси (Proxy Pattern) — это структурный паттерн проектирования, который предоставляет промежуточный объект (прокси) для контроля доступа к другому объекту. Прокси выступает как заместитель или плацдарм для контроля доступа к реальному объекту.
Основная идея
Вместо прямого обращения к объекту, клиент обращается к прокси, который:
- Может отложить создание реального объекта
- Контролирует доступ
- Логирует операции
- Добавляет кэширование
- Выполняет проверки безопасности
Простой пример
// Интерфейс, который реализуют и реальный объект, и прокси
public interface Document {
void open();
void close();
String getContent();
}
// Реальный объект — тяжёлый, дорого создавать
public class RealDocument implements Document {
private String filename;
private String content;
public RealDocument(String filename) {
this.filename = filename;
System.out.println("Загрузка документа: " + filename); // Дорогая операция
this.content = loadFromDisk(filename);
}
@Override
public void open() {
System.out.println("Открыт документ: " + filename);
}
@Override
public void close() {
System.out.println("Закрыт документ: " + filename);
}
@Override
public String getContent() {
return content;
}
private String loadFromDisk(String filename) {
// Дорогостоящая операция загрузки
return "Содержимое " + filename;
}
}
// Прокси — ленивая инициализация
public class DocumentProxy implements Document {
private String filename;
private RealDocument realDocument;
public DocumentProxy(String filename) {
this.filename = filename;
// Не создаём RealDocument, пока он не понадобится
}
// Инициализация при первом обращении
private RealDocument getRealDocument() {
if (realDocument == null) {
realDocument = new RealDocument(filename);
}
return realDocument;
}
@Override
public void open() {
getRealDocument().open();
}
@Override
public void close() {
getRealDocument().close();
}
@Override
public String getContent() {
return getRealDocument().getContent();
}
}
// Использование
Document doc = new DocumentProxy("report.pdf");
// Реальный документ ещё не загружен
doc.open(); // Теперь загружается
String content = doc.getContent();
doc.close();
Типы прокси
1. Ленивая инициализация (Lazy Initialization Proxy)
public class DatabaseProxy implements Database {
private RealDatabase realDb;
@Override
public ResultSet query(String sql) {
// Создание БД происходит только при первом запросе
if (realDb == null) {
realDb = new RealDatabase();
}
return realDb.query(sql);
}
}
2. Контроль доступа (Protection Proxy)
public interface BankAccount {
void withdraw(double amount);
double getBalance();
}
public class BankAccountProxy implements BankAccount {
private RealBankAccount realAccount;
private User currentUser;
public BankAccountProxy(RealBankAccount realAccount, User user) {
this.realAccount = realAccount;
this.currentUser = user;
}
@Override
public void withdraw(double amount) {
// Проверка прав доступа
if (!currentUser.hasPermission("withdraw")) {
throw new AccessDeniedException("Недостаточно прав");
}
// Проверка лимита
if (amount > realAccount.getBalance()) {
throw new InsufficientFundsException("Недостаточно средств");
}
realAccount.withdraw(amount);
}
@Override
public double getBalance() {
return realAccount.getBalance();
}
}
3. Логирование (Logging Proxy)
public class UserServiceProxy implements UserService {
private RealUserService realService;
private Logger logger = LoggerFactory.getLogger(UserServiceProxy.class);
@Override
public User getUserById(int id) {
logger.info("Запрос пользователя с ID: " + id);
long startTime = System.currentTimeMillis();
User user = realService.getUserById(id);
long duration = System.currentTimeMillis() - startTime;
logger.info("Пользователь найден за " + duration + "ms");
return user;
}
@Override
public void saveUser(User user) {
logger.info("Сохранение пользователя: " + user.getId());
realService.saveUser(user);
logger.info("Пользователь сохранён");
}
}
4. Кэширование (Caching Proxy)
public class CachedUserService implements UserService {
private RealUserService realService;
private Map<Integer, User> cache = new HashMap<>();
@Override
public User getUserById(int id) {
// Проверяем кэш
if (cache.containsKey(id)) {
System.out.println("Из кэша: " + id);
return cache.get(id);
}
// Загружаем из реального сервиса
System.out.println("Загрузка из БД: " + id);
User user = realService.getUserById(id);
cache.put(id, user);
return user;
}
@Override
public void clearCache() {
cache.clear();
}
}
5. Удалённый прокси (Remote Proxy)
public interface ImageService {
Image loadImage(String url);
}
public class RemoteImageProxy implements ImageService {
private HttpClient httpClient;
@Override
public Image loadImage(String url) {
// Загрузка через сеть
ResponseBody body = httpClient.newCall(
new Request.Builder().url(url).build()
).execute().body();
return new Image(body.bytes());
}
}
Реальный пример: Spring AOP прокси
public interface UserService {
User getUserById(int id);
void saveUser(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(int id) {
return new User(id, "John");
}
@Override
@Transactional // Spring создаст прокси для управления транзакциями
public void saveUser(User user) {
// Сохранение в БД
}
}
// Spring создаёт прокси, который:
// - Открывает транзакцию
// - Вызывает реальный метод
// - Коммитит или откатывает транзакцию
public class ProxyExample {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService service = context.getBean(UserService.class);
// service на самом деле прокси, а не UserServiceImpl
System.out.println(service.getClass()); // Proxy class
service.saveUser(new User(1, "Alice"));
}
}
Пример с аннотациями
@Service
public class OrderService {
@Cached(ttl = 3600) // Custom аннотация
public Order getOrder(int id) {
// Прокси будет кэшировать результат
return orderRepository.findById(id);
}
@RateLimited(maxRequests = 100)
public List<Order> getAllOrders() {
// Прокси ограничит количество запросов
return orderRepository.findAll();
}
@LogExecutionTime
public void createOrder(Order order) {
// Прокси залогирует время выполнения
orderRepository.save(order);
}
}
@Aspect
@Component
public class CachingAspect {
private Map<String, Object> cache = new HashMap<>();
@Around("@annotation(cached)")
public Object cache(ProceedingJoinPoint joinPoint, Cached cached) throws Throwable {
String key = joinPoint.getSignature() + Arrays.toString(joinPoint.getArgs());
if (cache.containsKey(key)) {
return cache.get(key);
}
Object result = joinPoint.proceed();
cache.put(key, result);
return result;
}
}
Плюсы и минусы
Плюсы
✅ Ленивая инициализация — создание объектов только при необходимости ✅ Контроль доступа — безопасность ✅ Логирование и мониторинг — отслеживание операций ✅ Кэширование — производительность ✅ Разделение ответственности — прокси занимается служебными операциями
Минусы
❌ Усложнение кода — дополнительный уровень абстракции ❌ Небольшое снижение производительности — дополнительный вызов ❌ Усложнение отладки — трудно отследить ошибки
Когда использовать
✅ Используй Proxy для:
- Ленивой инициализации дорогостоящих объектов
- Контроля доступа и безопасности
- Логирования и мониторинга
- Кэширования результатов
- Удалённого доступа к объектам
❌ Избегай, если:
- Нужна максимальная производительность
- Простое логирование можно сделать иначе
Сравнение с другими паттернами
| Паттерн | Назначение | Когда использовать |
|---|---|---|
| Proxy | Контроль доступа к объекту | Безопасность, логирование, кэш |
| Decorator | Добавление функциональности | Расширение возможностей |
| Facade | Упрощение интерфейса | Упрощение API |
| Adapter | Совместимость интерфейсов | Интеграция различных систем |
Заключение
Паттерн Proxy — это мощный инструмент для:
- Контроля доступа к объектам
- Логирования и мониторинга
- Оптимизации производительности через кэширование
- Ленивой инициализации
В Java паттерн широко используется в Spring Framework (через AOP) и в других фреймворках для создания прокси объектов.