← Назад к вопросам
Приведи пример использования паттерна прокси
1.8 Middle🔥 141 комментариев
#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Приведи пример использования паттерна прокси
Что такое паттерн Proxy?
Паттерн Прокси (Proxy Pattern) — это структурный паттерн проектирования, который предоставляет объект-заместитель вместо реального объекта. Прокси контролирует доступ к оригинальному объекту, позволяя выполнять дополнительные операции: логирование, кэширование, ленивую инициализацию, контроль доступа и т.д.
Основные сценарии использования
- Ленивая инициализация — создание дорогого объекта только при первом обращении
- Логирование и аудит — отслеживание всех обращений к объекту
- Кэширование — сохранение результатов вычислений
- Контроль доступа — проверка прав перед обращением
- Удаленные объекты — работа с объектами на других серверах
Практический пример: Прокси для базы данных
// Интерфейс сервиса
public interface UserService {
User getUserById(Long id);
void saveUser(User user);
}
// Реальная реализация
public class UserServiceImpl implements UserService {
private UserRepository repository;
@Override
public User getUserById(Long id) {
System.out.println("Запрос к БД: SELECT * FROM users WHERE id = " + id);
return repository.findById(id).orElse(null);
}
@Override
public void saveUser(User user) {
System.out.println("Сохранение пользователя: " + user.getName());
repository.save(user);
}
}
// Прокси с логированием и кэшированием
public class CachedUserServiceProxy implements UserService {
private final UserService realService;
private final Map<Long, User> cache = new HashMap<>();
private static final Logger logger = LoggerFactory.getLogger(CachedUserServiceProxy.class);
public CachedUserServiceProxy(UserService realService) {
this.realService = realService;
}
@Override
public User getUserById(Long id) {
logger.info("Попытка получить пользователя с ID: {}", id);
// Проверяем кэш
if (cache.containsKey(id)) {
logger.info("Найдено в кэше: {}", id);
return cache.get(id);
}
// Если в кэше нет, идём в БД
logger.info("Кэш miss, обращаемся к БД");
User user = realService.getUserById(id);
if (user != null) {
cache.put(id, user);
}
return user;
}
@Override
public void saveUser(User user) {
logger.info("Сохранение пользователя: {}", user.getName());
realService.saveUser(user);
// Инвалидируем кэш
cache.remove(user.getId());
}
}
// Использование
public class Main {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserService cachedService = new CachedUserServiceProxy(realService);
// Первый вызов — обращение к БД
User user1 = cachedService.getUserById(1L); // Из БД
// Второй вызов — возврат из кэша
User user2 = cachedService.getUserById(1L); // Из кэша
// Сохранение инвалидирует кэш
User newUser = new User(1L, "John");
cachedService.saveUser(newUser);
// Снова обращаемся к БД
User user3 = cachedService.getUserById(1L); // Из БД
}
}
Пример с контролем доступа
// Интерфейс
public interface AdminService {
void deleteUser(Long userId);
void blockUser(Long userId);
}
// Реальная реализация
public class AdminServiceImpl implements AdminService {
@Override
public void deleteUser(Long userId) {
System.out.println("Пользователь " + userId + " удалён");
}
@Override
public void blockUser(Long userId) {
System.out.println("Пользователь " + userId + " заблокирован");
}
}
// Прокси с проверкой прав доступа
public class SecureAdminServiceProxy implements AdminService {
private final AdminService realService;
private final User currentUser;
public SecureAdminServiceProxy(AdminService realService, User currentUser) {
this.realService = realService;
this.currentUser = currentUser;
}
@Override
public void deleteUser(Long userId) {
if (!currentUser.hasRole("SUPER_ADMIN")) {
throw new SecurityException("Нет прав для удаления пользователей");
}
realService.deleteUser(userId);
}
@Override
public void blockUser(Long userId) {
if (!currentUser.hasRole("ADMIN") && !currentUser.hasRole("SUPER_ADMIN")) {
throw new SecurityException("Нет прав для блокировки пользователей");
}
realService.blockUser(userId);
}
}
Ленивая инициализация с Proxy
public class LazyResourceProxy implements Resource {
private Resource realResource;
private boolean initialized = false;
@Override
public void load() {
if (!initialized) {
System.out.println("Инициализация ресурса...");
realResource = new HeavyResource();
initialized = true;
}
}
@Override
public String getData() {
load(); // Ленивая инициализация
return realResource.getData();
}
}
Динамический прокси через Java Reflection
// Создание прокси на лету
UserService realService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new InvocationHandler() {
@Override
public Object invoke(Object target, Method method, Object[] args) throws Throwable {
System.out.println("Вызов метода: " + method.getName());
long start = System.currentTimeMillis();
Object result = method.invoke(realService, args);
long duration = System.currentTimeMillis() - start;
System.out.println("Время выполнения: " + duration + "ms");
return result;
}
}
);
proxy.getUserById(1L); // Логирование и измерение времени автоматически
Сравнение с подобными паттернами
| Паттерн | Назначение | Проблема |
|---|---|---|
| Proxy | Контроль доступа, логирование | Требует интерфейса |
| Decorator | Добавление функционала | Может создавать цепочку |
| Facade | Упрощение интерфейса | Скрывает детали |
| Adapter | Совместимость интерфейсов | Конвертирует типы |
Ключевые преимущества
- Прозрачность — клиент не знает, что работает с прокси
- Гибкость — лёгко добавлять новые функции без изменения реального сервиса
- Разделение ответственности — бизнес-логика отделена от вспомогательной
- Безопасность — контроль доступа и аудит
Недостатки
- Производительность — дополнительный слой вызовов
- Сложность — может быть сложно отследить вызовы
- Требует интерфейса — нельзя использовать с финальными классами
Паттерн Proxy — это один из самых полезных паттернов в практической разработке, особенно при работе с внешними сервисами, БД и API.