Similar presentations:
Паттерны_проектирования_Facade_и_Proxy
1.
Паттерны проектированияFacade и Proxy
Структурные паттерны GoF: Фасад и Заместитель
Групповая презентация |
10-15 минут
2.
Содержание1
Введение
Структурные паттерны проектирования. Место Facade и Proxy в классификации
GoF.
3
Proxy (Заместитель)
Типы прокси, реализация на C++ и Java, примеры из STL, Spring, Hibernate.
2
Facade (Фасад)
Назначение, структура, реализация на C++ и Java, сценарии применения,
плюсы и минусы.
4
Сравнение и практика
Facade vs Proxy, анти-примеры и рефакторинг, когда применять и когда нет.
3.
Структурные паттерны проектированияКлассификация GoF: назначение и место Facade и Proxy среди
структурных паттернов
Adapter
Bridge
Facade
Proxy
4.
Facade: Назначение и структураGoF Structural Pattern
Определение GoF
UML-диаграмма классов
«Предоставляет унифицированный интерфейс к набору интерфейсов подсистемы.
Facade определяет высокоуровневый интерфейс, который упрощает
использование подсистемы.»
Участники
Facade
делегирует
Subsystem A
Facade: Знает, какие классы подсистемы отвечают за выполнение запроса;
делегирует запросы клиента объектам подсистемы.
Subsystem B
Subsystem C
использует
Client
Subsystem classes: Реализуют функциональность подсистемы; выполняют работу,
назначенную объектом Facade.
Client: Взаимодействует с подсистемой через Facade.
Когда применять
• Сложная подсистема с множеством классов
• Необходимость слоистой архитектуры
• Множество клиентов используют одну подсистему
Когда НЕ применять
Простая система (over-engineering), необходимость низкоуровневого контроля над каждым шагом
подсистемы.
5.
Facade: Реализация на C++Подсистема компилятора
// Подсистема: Lexer, Parser, CodeGen
classLexer { public: tokenize(...); };
classParser { public: parse(...); };
classCodeGenerator { public: generate(...); };
C++
Клиентский код
// До: клиент управляет всеми подсистемами
Lexer l; Parser p; CodeGenerator g;
auto t = l.tokenize(src);
auto a = p.parse(t);
g.generate(a);
// После: единый вызов через Facade
Класс Facade
CompilerFacade compiler;
compiler.compile(src);
classCompilerFacade {
public:
boolcompile(conststd::string& src) {
auto tokens = lexer.tokenize(src);
auto ast = parser.parse(tokens);
return codegen.generate(ast);
}
private:
Lexer lexer; Parser parser;
CodeGenerator codegen;
};
Реальные примеры
QApplication
скрывает сложность инициализации GUI-подсистемы, event loop,
Qt:
platform abstraction
Boost: boost::asio::io_context как фасад над системными вызовами epoll/kqueue/IOCP
STL: std::fstream — фасад над FILE* и системными дескрипторами
6.
Facade: Реализация на JavaE-commerce подсистема
Java
Сравнение C++ vs Java
Аспект
C++
Java
public booleancheckStock(String product) { ... }
Управление памятью
RAII, unique_ptr
GC
}
Коллекции
STL containers
JDK Collections
public classPaymentService {
Абстракция
Шаблоны
Generics
public classInventoryService {
public booleanprocess(double amount) { ... }
}
public classShippingService {
public voidship(String order) { ... }
}
Реальные примеры
JdbcTemplate как фасад над низкоуровневым JDBC API, скрывающий try-catchSpring:
finally
Hibernate:
Session как фасад для работы с persistence context, transaction boundaries
OrderServiceFacade
public classOrderFacade {
privateInventoryService inv = new ...;
privatePaymentService pay = new ...;
privateShippingService ship = new ...;
public booleanplaceOrder(String product, double amount) {
if (!inv.checkStock(product)) return false;
if (!pay.process(amount)) return false;
ship.ship(product);
return true;
}
}
Java EE: EJB Home Interface упрощал доступ к enterprise beans
7.
Facade: Сценарии, плюсы и минусыСценарии применения
Плюсы
Минусы
API Gateway — единая точка входа в
микросервисную архитектуру
+ Изоляция сложности — клиент работает с простым
API
− God Object — риск создания «божественного
объекта», знающего всё
SDK — обёртка над сторонними библиотеками
(ffmpeg, OpenCV)
+ Снижение связанности — клиент не зависит от
подсистемы
− Дополнительный слой — абстракция требует
поддержки
Service Layer — слой бизнес-логики в
многоуровневой архитектуре
+ Упрощение тестирования — мокаем один фасад
вместо множества классов
− Снижение гибкости — при необходимости тонкого
контроля фасад мешает
+ Защита от изменений — изменения в подсистеме
изолированы
Признаки необходимости
• Клиент зависит от 5+ классов подсистемы
• Изменения в подсистеме ломают клиентский код
• Необходимость кросс-платформенной абстракции
8.
Паттерн Proxy (Заместитель)Управление доступом к объекту через суррогат
Virtual
Remote
Protection
Smart
9.
Proxy: Назначение и структураGoF Structural Pattern
Определение GoF
UML-диаграмма классов
«Предоставляет заместителя или местозаполнителя для другого объекта с целью
контроля доступа к нему.»
<>
Subject
+request()
Типы прокси
▲
Virtual
Remote
Ленивая инициализация тяжёлых объектов
Представление удалённого объекта
Protection
Smart
Контроль доступа по правам
Доп. действия: счётчики, логи
▲
RealSubject
Proxy
+request()
+request()
использует
Client
Участники
Subject: Общий интерфейс для Proxy и RealSubject
RealSubject: Реальный объект, доступ к которому контролируется
Proxy: Суррогат, реализующий тот же интерфейс; управляет доступом
Когда применять
• Дорогостоящие объекты (ленивая инициализация)
• Удалённые сервисы (RMI, gRPC)
• Безопасность, логирование, кэширование
10.
Proxy: Реализация на C++Virtual Proxy: ленивая загрузка изображений
C++
Использование
classImage {
ProxyImage img("photo.jpg");
public:
// RealImage НЕ создан!
virtualvoiddisplay() = default;
img.display(); // здесь происходит загрузка
virtual ~Image() = default;
img.display(); // повторный вызов — быстро
};
classRealImage : publicImage {
std::string filename;
voidload() { /* тяжёлая операция */ }
public:
RealImage(conststd::string& f) : filename(f) { load(); }
voiddisplay() override { ... }
};
Реальные примеры
std::unique_ptr
и std::shared_ptr — прокси для управления временем жизни объектов
STL Smart Pointers:
через перегрузку operator-> и operator*
Qt: QPointer — защитный прокси для QObject, обнуляется при удалении объекта
Pointer to Implementation — скрытый прокси для разделения интерфейса и
PIMPL:
реализации
ProxyImage с отложенной инициализацией
classProxyImage : publicImage {
std::string filename;
std::unique_ptr<RealImage> realImage;
public:
ProxyImage(conststd::string& f) : filename(f) {}
voiddisplay() override {
if (!realImage) {
realImage = std::make_unique<RealImage>(filename);
}
realImage->display();
}
};
11.
Proxy: Реализация на JavaProtection Proxy: контроль доступа
public interfaceInternetAccess {
voidconnect(String host);
}
public classProxyInternetimplementsInternetAccess {
Java
Сравнение C++ vs Java
Аспект
C++
Java
Прокси
Статические классы, шаблоны
Рефлексия, Dynamic Proxy
Виртуальность
vtable, виртуальные методы
Интерфейсы + рефлексия
Управление памятью
RAII, smart pointers
GC, слабые ссылки
privateRealInternet real;
privateString role;
public voidconnect(String host) {
if (!isAllowed(host, role))
throw newSecurityException();
if (real == null) real = newRealInternet();
real.connect(host);
}
}
Реальные примеры
JDK
Dynamic
Spring
AOP: Proxy и CGLIB для транзакций, безопасности,
кэширования
Lazy loading — прокси-сущности подгружаются из БД только при первом
Hibernate:
доступе
Mockito: Mock-объекты создаются как прокси через CGLIB или ByteBuddy
Java RMI: Stub — remote proxy для вызова методов на удалённой JVM
JDK Dynamic Proxy
InvocationHandler handler = newLoggingHandler(target);
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
newClass[]{Service.class},
handler);
proxy.execute(); // логирование + вызов метода
12.
Proxy: Сценарии, плюсы и минусыСценарии применения
Плюсы
Минусы
Кэширование — прокси хранит результаты
дорогостоящих операций
+ Прозрачность — клиент не знает о существовании
прокси
− Усложнение — дополнительные классы и
индиректность
Логирование — перехват вызовов для аудита
+ Отложенная инициализация — экономия ресурсов
RBAC — контроль доступа по ролям
+ Cross-cutting concerns — логи, транзакции,
безопасность отделены от бизнес-логики
− Производительность — оверхед на перехват
вызовов
Lazy Init — JDBC Connection, SessionFactory
+ Безопасность — централизованный контроль
доступа
Признаки необходимости
• Объект требует много ресурсов при создании
• Доступ должен контролироваться/логироваться
• Нужно прозрачное добавление сквозной логики
− Отладка — стек вызовов через прокси усложняет
траблшутинг
− Циклические зависимости — риск при
неосторожном использовании
13.
Facade vs Proxy: Ключевые различияСравнительная таблица
Аспект
Facade
Сходства
Proxy
Назначени Упрощение сложной
е
подсистемы
Контроль доступа к объекту
Интерфейс Новый, упрощённый API
Тот же интерфейс, что у реального
объекта
Масштаб
One-to-many (подсистема)
One-to-one (объект)
Действие
Оркестрация вызовов
Делегирование + доп. логика
Клиент
Знает о Facade
Может не знать о Proxy
Пример
CompilerFacade
Smart Pointer
• Оба являются структурными обёртками
• Снижают связанность клиентского кода
• Предоставляют посредника между клиентом и реализацией
• Упрощают взаимодействие с подсистемами
Ключевое различие
Facade создаёт новый упрощённый API для сложной подсистемы, в то время как
Proxy сохраняет тот же API, добавляя прозрачную функциональность.
Когда выбрать какой
Facade: Интеграция сложных подсистем, SDK, API Gateway
Proxy: Управление доступом, ленивая загрузка, логирование, кэширование
14.
Анти-примеры и рефакторингFacade анти-пример
Клиент напрямую вызывает 7 методов разных сервисов для оформления заказа:
Proxy анти-пример
Создание тяжёлого объекта при старте приложения, хотя он нужен раз в день:
// Запутанный код, высокая связанность
// Медленный старт, избыточное потребление памяти
inv.check(p); pay.validate(c);
classApp {
pay.charge(a); tax.calculate(a);
ReportGenerator generator; // конструируется сразу!
ship.schedule(p); notify.send(u);
// занимает 500MB и 30 секунд инициализации
log.record(...);
};
Рефакторинг
Введение OrderFacade с единым методом:
Рефакторинг
Введение ReportGeneratorProxy с ленивой инициализацией:
OrderFacade facade;
classReportProxy : publicIReport {
facade.placeOrder(product, amount);
std::unique_ptr<ReportGenerator> real;
// Вся оркестрация скрыта внутри
voidgenerate() override {
if (!real) real = std::make_unique...;
real->generate();
}
};
Общие ошибки и решения
God Object: Facade разрастается → разбиение на специализированные фасады (UserFacade,
OrderFacade)
Избыточная логика: Proxy делает слишком много → чёткое разделение ответственностей,
выделение декораторов
15.
ЗаключениеFacade
Proxy
Упрощает работу со сложными подсистемами,
предоставляя высокоуровневый интерфейс. Изолирует
клиента от деталей реализации.
Контролирует доступ к объектам, добавляя прозрачную
функциональность: ленивую загрузку, кэширование,
безопасность.
Вопросы для самопроверки
• В каких фреймворках вы уже
встречали эти паттерны?
• Как отличить Facade от Proxy в
существующем коде?
• Когда применение паттерна
становится over-engineering?
Спасибо за внимание! Готовы ответить на ваши вопросы.
programming