Similar presentations:
Proxy + поведенческие паттерны. Урок 9-10
1. Proxy + Поведенческие паттерны
PROXY + ПОВЕДЕНЧЕСКИЕПАТТЕРНЫ
Урок 9-10
2. Теория: Proxy (структурный паттерн)
ТЕОРИЯ: PROXY (СТРУКТУРНЫЙПАТТЕРН)
• Что такое Proxy
• Proxy (Заместитель) — это объект, который “стоит между” клиентом и
реальным объектом и контролирует доступ к нему.
• То есть клиент работает не напрямую с RealObject, а через Proxy.
3. Когда Proxy реально нужен
КОГДА PROXY РЕАЛЬНО НУЖЕНProxy полезен, когда:
• Объект дорогой в создании
Например:
• загрузка изображения с диска
• подключение к API
• запрос в базу
• генерация PDF
Proxy делает ленивую загрузку (Lazy loading)
4. Когда Proxy реально нужен
КОГДА PROXY РЕАЛЬНО НУЖЕННужно ограничить доступ
Например:
• только админ может удалить заказ
• только авторизованный пользователь может оплатить
Proxy = защита (Protection Proxy)
5. Когда Proxy реально нужен
КОГДА PROXY РЕАЛЬНО НУЖЕННужно логирование
Например:
• логировать каждую операцию “Оплата заказа”
• считать статистику
Proxy = logging proxy
6. Когда Proxy реально нужен
КОГДА PROXY РЕАЛЬНО НУЖЕННужно кеширование
Например:
• запрос “каталог товаров” одинаковый
• запрос “прайс” повторяется
Proxy хранит результат
7. Участники паттерна Proxy
УЧАСТНИКИ ПАТТЕРНА PROXY• Subject — общий интерфейс
• RealSubject — реальный объект, который делает работу
• Proxy — объект-заместитель, который:
• хранит ссылку на RealSubject
• решает, когда создавать/вызывать RealSubject
• Client — работает через Subject
8. Мини-пример в контексте магазина
МИНИ-ПРИМЕР В КОНТЕКСТЕМАГАЗИНА
• Сервис каталога товаров:
• RealCatalogService делает запрос к базе (дорого)
• ProxyCatalogService кеширует результат на 10 секунд
9. Важное отличие от Decorator
ВАЖНОЕ ОТЛИЧИЕ ОТ DECORATORОба “оборачивают объект”, но:
• Decorator добавляет функциональность и обычно их можно “наслаивать”
• Proxy контролирует доступ и часто скрывает факт существования
RealObject
10. Product — модель данных (не участник паттерна)
PRODUCT — МОДЕЛЬ ДАННЫХ (НЕУЧАСТНИК ПАТТЕРНА)
• Это просто сущность предметной области.
public record Product(int Id, string Name, decimal
Price);
11. Subject (общий интерфейс) — IProductCatalog
SUBJECT (ОБЩИЙ ИНТЕРФЕЙС) —IPRODUCTCATALOG
• Роль в Proxy: Subject определяет общий интерфейс, с которым работает
клиент.
public interface IProductCatalog
{
IReadOnlyList<Product> GetProducts();
}
Важно:
• И RealProductCatalog, и CachedProductCatalogProxy реализуют этот
интерфейс.
• Это позволяет клиенту не знать, работает он с реальным объектом или с
Proxy.
12. RealSubject (реальный объект) — RealProductCatalog
REALSUBJECT (РЕАЛЬНЫЙ ОБЪЕКТ) —REALPRODUCTCATALOG
public class RealProductCatalog : IProductCatalog
{
public IReadOnlyList<Product> GetProducts()
{
Console.WriteLine("[RealProductCatalog] Loading products from DB...");
Thread.Sleep(1000); // имитация дорогой операции
return new List<Product>
{
new Product(1, "Keyboard", 2500),
new Product(2, "Mouse", 1200),
new Product(3, "Monitor", 19990),
};
}
}
13. RealSubject (реальный объект) — RealProductCatalog
REALSUBJECT (РЕАЛЬНЫЙ ОБЪЕКТ) —REALPRODUCTCATALOG
• RealSubject — объект, который делает настоящую работу.
• Здесь “дорогая операция” — это Thread.Sleep(1000)
• (как будто запрос в базу или API).
14. Proxy (заместитель) — CachedProductCatalogProxy
PROXY (ЗАМЕСТИТЕЛЬ) —CACHEDPRODUCTCATALOGPROXY
• Proxy контролирует доступ к RealSubject.
• В этом примере Proxy выполняет задачу: кеширование результата на 5
секунд (TTL)
Ключевой момент:
Proxy решает:
• можно ли вернуть кеш
• или нужно идти в RealSubject
15. Proxy (заместитель) — CachedProductCatalogProxy
PROXY (ЗАМЕСТИТЕЛЬ) —CACHEDPRODUCTCATALOGPROXY
public class CachedProductCatalogProxy :
IProductCatalog
public IReadOnlyList<Product> GetProducts()
{
{
private readonly IProductCatalog _real;
ссылка на RealSubject
//
private IReadOnlyList<Product>? _cache;
кеш результата
//
private DateTime _cacheTime;
время кеширования
//
if (_cache != null && DateTime.Now _cacheTime < _ttl)
{
Console.WriteLine("[Proxy] Return
products from cache");
return _cache;
}
private readonly TimeSpan _ttl =
TimeSpan.FromSeconds(5);
Console.WriteLine("[Proxy] Cache is empty or
expired -> call RealProductCatalog");
_cache = _real.GetProducts();
public CachedProductCatalogProxy(IProductCatalog
real)
_cacheTime = DateTime.Now;
{
return _cache;
_real = real;
}
}
}
16. Client (клиент) — будет в коде запуска
CLIENT (КЛИЕНТ) — БУДЕТ В КОДЕЗАПУСКА
Роль: Клиент работает с интерфейсом IProductCatalog и не обязан знать,
что там Proxy.
• 1-й вызов → реальная загрузка (1 сек)
• 2-й вызов → кеш (мгновенно)
• ждём 6 секунд → кеш протух
• 3-й вызов → снова реальная загрузка
17. Client (клиент) — будет в коде запуска
CLIENT (КЛИЕНТ) — БУДЕТ В КОДЕЗАПУСКА
public class Program
{
===");
public static void Main()
Console.WriteLine("\n=== Wait 6 seconds (cache expires)
Thread.Sleep(6000);
{
// Client работает через интерфейс Subject
IProductCatalog catalog = new
CachedProductCatalogProxy(
===");
Console.WriteLine("\n=== Third call (cache expired)
var products3 = catalog.GetProducts();
new RealProductCatalog()
Print(products3);
);
}
Console.WriteLine("=== First call ===");
private static void Print(IReadOnlyList<Product> products)
var products1 = catalog.GetProducts();
{
Print(products1);
foreach (var p in products)
Console.WriteLine($"{p.Id}. {p.Name} - {p.Price}");
Console.WriteLine("\n=== Second call (cache) ===");
var products2 = catalog.GetProducts();
Print(products2);
}
}
18. Вывод
ВЫВОДClient вызывает GetProducts()
но:
• иногда запрос реально идёт в RealProductCatalog
• иногда Proxy возвращает кеш
• Client при этом не меняется вообще
19. Поведенческие паттерны
ПОВЕДЕНЧЕСКИЕ ПАТТЕРНЫ20. Что значит поведенческий?
ЧТО ЗНАЧИТ ПОВЕДЕНЧЕСКИЙ?• Паттерны поведения управляют различными вариантами поведения
системы объектов
21. Chain of Responsibility (Цепочка обязанностей)
CHAIN OF RESPONSIBILITY (ЦЕПОЧКАОБЯЗАННОСТЕЙ)
• Объект отправляет запрос, не зная, кто его обработает.
Запрос проходит по цепочке обработчиков, пока кто-то не обработает
• Участники
• Handler — интерфейс обработки
• ConcreteHandler — конкретный обработчик
• successor — ссылка на следующий обработчик
• Client — инициирует запрос
22. Пример в магазине
ПРИМЕР В МАГАЗИНЕ• Проверка заказа перед оплатой:
• Цепочка:
• Проверка наличия товаров
• Проверка возраста (если товар 18+)
• Проверка баланса / лимита
• Проверка доставки
23. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class Order
{
public int CustomerAge { get; set; }
public List<Product> Items { get; set; } = new();
public decimal Total => Items.Sum(i => i.Price);
}
24. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic abstract class OrderCheckHandler
{
{
if (!Check(order))
protected OrderCheckHandler? Next;
throw new
Exception($"{GetType().Name}: check
failed!");
public OrderCheckHandler
SetNext(OrderCheckHandler next)
Next?.Handle(order);
{
}
Next = next;
return next;
}
protected abstract bool Check(Order
order);
}
public void Handle(Order order)
25. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class StockCheckHandler : OrderCheckHandler
{
protected override bool Check(Order order)
{
Console.WriteLine("[StockCheck] OK (demo always
true)");
return order.Items.Count > 0;
}
}
26. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class MinPriceCheckHandler : OrderCheckHandler
{
private readonly decimal _minPrice;
public MinPriceCheckHandler(decimal minPrice)
{
_minPrice = minPrice;
}
protected override bool Check(Order order)
{
Console.WriteLine($"[MinPriceCheck] Total={order.Total}, Min={_minPrice}");
return order.Total >= _minPrice;
}
}
27. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class AgeCheckHandler : OrderCheckHandler
{
protected override bool Check(Order order)
{
bool has18Plus = order.Items.Any(p => p.Name.Contains("18+"));
if (!has18Plus)
{
Console.WriteLine("[AgeCheck] No 18+ items -> OK");
return true;
}
Console.WriteLine($"[AgeCheck] CustomerAge={order.CustomerAge}");
return order.CustomerAge >= 18;
}
}
28. Command (Команда)
COMMAND (КОМАНДА)• Команда — это объект.
Она инкапсулирует действие, позволяет:
• ставить команды в очередь
• отменять
• выполнять по расписанию
• Участники
• Command — интерфейс Execute()
• ConcreteCommand — конкретная команда
• Receiver — тот, кто реально выполняет действие
• Invoker — вызывает Execute()
29. Пример в магазине
ПРИМЕР В МАГАЗИНЕ• Команды:
• AddToCartCommand
• RemoveFromCartCommand
• ApplyPromoCommand
• CheckoutCommand
• Можно сделать undo для корзины.
30. Command — команды корзины
COMMAND — КОМАНДЫ КОРЗИНЫ• Command = объект действия
• Receiver = Cart
• Invoker = CartController
31. Command — пример
COMMAND — ПРИМЕРpublic class Cart
public void Clear()
{
{
private readonly List<Product> _items = new();
_items.Clear();
Console.WriteLine("[Cart] Cleared");
public void Add(Product p)
}
{
_items.Add(p);
public void Print()
Console.WriteLine($"[Cart] Added: {p.Name}");
{
}
Console.WriteLine("Cart:");
foreach (var item in _items)
public void Remove(Product p)
Console.WriteLine($" - {item.Name} ({item.Price})");
{
_items.Remove(p);
Console.WriteLine($"Total: {_items.Sum(i => i.Price)}");
Console.WriteLine($"[Cart] Removed: {p.Name}");
}
}
}
32. Command — пример
COMMAND — ПРИМЕРpublic interface ICommand
{
void Execute();
}
33. Command — пример
COMMAND — ПРИМЕРpublic class AddToCartCommand : ICommand
{
private readonly Cart _cart;
private readonly Product _product;
public AddToCartCommand(Cart cart, Product product)
{
_cart = cart;
_product = product;
}
public void Execute() => _cart.Add(_product);
}
34. Command — пример
COMMAND — ПРИМЕРpublic class AddToCartCommand : ICommand
{
private readonly Cart _cart;
private readonly Product _product;
public AddToCartCommand(Cart cart, Product product)
{
_cart = cart;
_product = product;
}
public void Execute() => _cart.Add(_product);
}
35. Command — пример
COMMAND — ПРИМЕРpublic class RemoveFromCartCommand : ICommand
{
private readonly Cart _cart;
private readonly Product _product;
public RemoveFromCartCommand(Cart cart, Product product)
{
_cart = cart;
_product = product;
}
public void Execute() => _cart.Remove(_product);
}
36. Command — пример
COMMAND — ПРИМЕРpublic class RemoveFromCartCommand : ICommand
{
private readonly Cart _cart;
private readonly Product _product;
public RemoveFromCartCommand(Cart cart, Product product)
{
_cart = cart;
_product = product;
}
public void Execute() => _cart.Remove(_product);
}
37. Command — пример
COMMAND — ПРИМЕРpublic class ClearCartCommand : ICommand
{
private readonly Cart _cart;
public ClearCartCommand(Cart cart)
{
_cart = cart;
}
public void Execute() => _cart.Clear();
}
38. Command — пример
COMMAND — ПРИМЕРpublic class CartController // Invoker
{
public void Run(ICommand command)
{
Console.WriteLine("[Invoker] Execute
command...");
command.Execute();
}
}
39. State (Состояние)
STATE (СОСТОЯНИЕ)• Состояния объекта выделяются в отдельные классы.
Каждый класс состояния реализует поведение по-своему
• Участники
• Context — объект, у которого есть состояние
• State — интерфейс поведения
• ConcreteState — конкретные состояния
40. Пример в магазине
ПРИМЕР В МАГАЗИНЕ• Заказ может быть:
• New
• Paid
• Shipped
• Delivered
• Cancelled
• И в каждом состоянии:
• разрешены разные действия
• разные переходы
41. State (Состояние) - Пример
STATE (СОСТОЯНИЕ) - ПРИМЕРЗаказ меняет поведение в зависимости от состояния.
public interface IOrderState
{
void Pay(OrderContext order);
void Ship(OrderContext order);
void Deliver(OrderContext order);
void Cancel(OrderContext order);
string Name { get; }
}
42. State (Состояние) - Пример
STATE (СОСТОЯНИЕ) - ПРИМЕРpublic class OrderContext
{
private IOrderState _state;
public OrderContext()
{
_state = new
CreatedState();
}
public void
SetState(IOrderState state)
{
_state = state;
Console.WriteLine($"[Order]
State -> {_state.Name}");
}
public void Pay() =>
_state.Pay(this);
public void Ship() =>
_state.Ship(this);
public void Deliver() =>
_state.Deliver(this);
public void Cancel() =>
_state.Cancel(this);
}
43. State (Состояние) - Пример
STATE (СОСТОЯНИЕ) - ПРИМЕРpublic class CreatedState : IOrderState
paid");
{
public string Name => "Created";
public void Pay(OrderContext order)
public void Deliver(OrderContext order) =>
Console.WriteLine("[Created] Cannot deliver: not
shipped");
{
public void Cancel(OrderContext order)
Console.WriteLine("[Created] Payment accepted");
{
order.SetState(new PaidState());
Console.WriteLine("[Created] Cancelled");
}
order.SetState(new CancelledState());
public void Ship(OrderContext order) =>
Console.WriteLine("[Created] Cannot ship: not
}
}
44. State (Состояние) - Пример
STATE (СОСТОЯНИЕ) - ПРИМЕРpublic class PaidState : IOrderState
{
public string Name => "Paid";
=>
public void Deliver(OrderContext order)
Console.WriteLine("[Paid] Cannot
deliver: not shipped");
public void Pay(OrderContext order) =>
paid");
Console.WriteLine("[Paid] Already
public void Cancel(OrderContext order)
{
public void Ship(OrderContext order)
{
Console.WriteLine("[Paid]
Shipped");
Console.WriteLine("[Paid] Cancelled
(refund)");
order.SetState(new
CancelledState());
order.SetState(new ShippedState()); }
}
}
45. State (Состояние) - Пример
STATE (СОСТОЯНИЕ) - ПРИМЕРpublic class ShippedState : IOrderState
{
public string Name => "Shipped";
=>
public void Pay(OrderContext order)
public void Deliver(OrderContext
order)
{
Console.WriteLine("[Shipped]
Delivered");
order.SetState(new
DeliveredState());
Console.WriteLine("[Shipped]
Already paid");
=>
}
public void Cancel(OrderContext
public void Ship(OrderContext order) order) =>
Console.WriteLine("[Shipped]
Already shipped");
Console.WriteLine("[Shipped]
Cannot cancel: already shipped");
}
46. State (Состояние) - Пример
STATE (СОСТОЯНИЕ) - ПРИМЕРpublic class DeliveredState :
IOrderState
{
public string Name => "Delivered";
public void Deliver(OrderContext
order) =>
Console.WriteLine("[Delivered]
Already delivered");
public void Pay(OrderContext order)
=>
Console.WriteLine("[Delivered]
Already finished");
public void Ship(OrderContext order)
=>
Console.WriteLine("[Delivered]
Already finished");
public void Cancel(OrderContext
order) =>
Console.WriteLine("[Delivered]
Cannot cancel: already delivered");
}
47. State (Состояние) - Пример
STATE (СОСТОЯНИЕ) - ПРИМЕРpublic class CancelledState :
IOrderState
{
public string Name => "Cancelled";
public void Deliver(OrderContext
order) =>
Console.WriteLine("[Cancelled]
Cannot deliver: cancelled");
public void Pay(OrderContext order)
=>
Console.WriteLine("[Cancelled]
Cannot pay: cancelled");
public void Ship(OrderContext order)
=>
Console.WriteLine("[Cancelled]
Cannot ship: cancelled");
public void Cancel(OrderContext
order) =>
Console.WriteLine("[Cancelled]
Already cancelled");
}
48. Template Method (Шаблонный метод)+пример
TEMPLATE METHOD (ШАБЛОННЫЙМЕТОД)+ПРИМЕР
Есть абстрактный класс с алгоритмом (скелетом).
Часть шагов реализуют наследники
Процесс оплаты:
Алгоритм:
ValidateOrder()
ReserveProducts()
Pay()
SendReceipt()
Наследники:
CardPaymentProcessor
CryptoPaymentProcessor
CashPaymentProcessor
49. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРЕсть скелет алгоритма “Checkout”, шаги
переопределяются.
public abstract class CheckoutProcess
protected virtual void Reserve(Order
order)
{
{
public void Checkout(Order order)
{
Console.WriteLine("[Checkout]
Reserve items");
}
Validate(order);
Reserve(order);
Pay(order);
protected abstract void Pay(Order
order);
Notify(order);
}
protected virtual void Validate(Order
order)
{
Console.WriteLine("[Checkout]
Validate order");
}
protected virtual void Notify(Order
order)
{
Console.WriteLine("[Checkout] Send
notification");
}
}
50. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class CardCheckout : CheckoutProcess
{
protected override void Pay(Order order)
{
Console.WriteLine("[CardCheckout] Pay by
card");
}
}
51. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class CashCheckout : CheckoutProcess
{
protected override void Pay(Order order)
{
Console.WriteLine("[CashCheckout] Pay by cash
on delivery");
}
}
52. Mediator (Посредник)+Пример в магазине
MEDIATOR (ПОСРЕДНИК)+ПРИМЕР ВМАГАЗИНЕ
• Вместо “хаоса связей” между объектами — один объект Mediator
координирует изменения состояний группы объектов
• Компоненты UI или подсистемы:
• Cart
• Warehouse
• Payment
• Delivery
• Вместо того чтобы Cart напрямую дергал Payment и Warehouse — есть
OrderMediator.
53. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic interface IShopMediator
{
void Notify(object sender, string ev);
}
54. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class CartSubsystem
{
private readonly IShopMediator _mediator;
public CartSubsystem(IShopMediator mediator)
{
_mediator = mediator;
}
public void Checkout()
{
Console.WriteLine("[Cart] Checkout clicked");
_mediator.Notify(this, "checkout");
}
}
55. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class WarehouseSubsystem
{
public bool ReserveItems()
{
Console.WriteLine("[Warehouse] Items
reserved");
return true;
}
}
56. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class PaymentSubsystem
{
public bool Pay()
{
Console.WriteLine("[Payment] Payment OK");
return true;
}
}
57. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class DeliverySubsystem
{
public void CreateDelivery()
{
Console.WriteLine("[Delivery] Delivery
created");
}
}
58. Практический пример
ПРАКТИЧЕСКИЙ ПРИМЕРpublic class OrderMediator : IShopMediator
Console.WriteLine("[Mediator] Start checkout process");
{
private readonly WarehouseSubsystem _warehouse;
if (!_warehouse.ReserveItems())
private readonly PaymentSubsystem _payment;
{
private readonly DeliverySubsystem _delivery;
Console.WriteLine("[Mediator] Cannot reserve items");
return;
public OrderMediator(
}
WarehouseSubsystem warehouse,
PaymentSubsystem payment,
if (!_payment.Pay())
DeliverySubsystem delivery)
{
{
Console.WriteLine("[Mediator] Payment failed");
_warehouse = warehouse;
return;
_payment = payment;
}
_delivery = delivery;
}
_delivery.CreateDelivery();
Console.WriteLine("[Mediator] Order completed!");
public void Notify(object sender, string ev)
}
{
}
if (ev == "checkout")
{
}
59. итог
ИТОГProxy
• Зачем: контроль доступа к объекту.
• Применение: кеш, защита, логирование, ленивая
загрузка.
Chain of Responsibility
• Зачем: передавать запрос по цепочке обработчиков.
• Применение: проверки, фильтры, middleware.
Command
• Зачем: превратить действие в объект.
• Применение: undo/redo, очередь команд, макросы.
State
• Зачем: вынести поведение по состояниям в отдельные
классы.
• Применение: заказ, доставка, пользовательские
статусы.
Template Method
• Зачем: общий алгоритм + разные шаги.
• Применение: обработка платежей, оформление
заказа, импорт данных.
Mediator
• Зачем: уменьшить связность между объектами.
• Применение: координация подсистем
(корзина/оплата/склад).
60. Выполни практическое задание на выбор
ВЫПОЛНИ ПРАКТИЧЕСКОЕЗАДАНИЕ НА ВЫБОР
61. Практическое задание
ПРАКТИЧЕСКОЕ ЗАДАНИЕ• Proxy + State (Заказ и его жизненный цикл)
State Сделать заказ с состояниями:
• Created
В разных состояниях методы ведут себя поразному.
2) Proxy Сделать OrderServiceProxy, который:
• Paid
• проверяет авторизацию пользователя
• Shipped
• логирует вызовы методов
• Delivered
• только потом вызывает OrderService
• Cancelled
Методы:
• Pay()
• Ship()
• Deliver()
• Cancel()
62. Практическое задание
ПРАКТИЧЕСКОЕ ЗАДАНИЕ• Chain of Responsibility + Template Method (Оформление заказа)
1) Chain of Responsibility Сделать цепочку проверок перед оплатой:
CheckStockHandler
CheckMinOrderPriceHandler
CheckAgeHandler (если товар 18+)
CheckDeliveryHandler
2) Template Method Сделать базовый класс CheckoutProcess:
Validate()
Reserve()
Pay()
Notify()
Наследники:
PickupCheckout
DeliveryCheckout
63. Практическое задание
ПРАКТИЧЕСКОЕ ЗАДАНИЕ• Mediator + Command (Оркестратор заказа)
1) Mediator Есть подсистемы:
2) Command Команды:
• PlaceOrderCommand
• Cart
• PayOrderCommand
• Warehouse
• CancelOrderCommand
• Payment
• Delivery
• Они не должны общаться напрямую.
• Только через OrderMediator.
• Invoker вызывает команды, команды дергают
Mediator.
64. Практическое задание
ПРАКТИЧЕСКОЕ ЗАДАНИЕ• Proxy + Chain of Responsibility (Защита и проверки)
1) Proxy Сделать AdminPanelProxy, который:
• проверяет роль пользователя (admin/user)
• если user → запрет на удаление товара
2) Chain of Responsibility Удаление товара проходит цепочку:
• ValidateIdHandler
• CheckPermissionsHandler
• CheckProductExistsHandler
• DeleteProductHandler
programming