Читати книгу - "Занурення в патерни проектування, Олександр Швець"
Шрифт:
Інтервал:
Добавити в закладку:
Замісник — це структурний патерн проектування, що дає змогу підставляти замість реальних об’єктів спеціальні об’єкти-замінники. Ці об’єкти перехоплюють виклики до оригінального об’єкта, дозволяючи зробити щось до чи після передачі виклику оригіналові.
ПроблемаДля чого взагалі контролювати доступ до об’єктів? Розглянемо такий приклад: у вас є зовнішній ресурсоємний об’єкт, який потрібен не весь час, а лише зрідка.
Запити до бази даних можуть бути дуже повільними.
Ми могли б створювати цей об’єкт не на самому початку програми, а тільки тоді, коли він реально кому-небудь знадобиться. Кожен клієнт об’єкта отримав би деякий код відкладеної ініціалізації. Це, ймовірно, призвело б до дублювання великої кількості коду.
В ідеалі цей код хотілося б помістити безпосередньо до службового класу, але це не завжди можливо. Наприклад, код класу може знаходитися в закритій сторонній бібліотеці.
РішенняПатерн Замісник пропонує створити новий клас-дублер, який має той самий інтерфейс, що й оригінальний службовий об’єкт. При отриманні запиту від клієнта об’єкт-замісник сам би створював примірник службового об’єкта та переадресовував би йому всю реальну роботу.
Замісник «прикидається» базою даних, прискорюючи роботу внаслідок ледачої ініціалізації і кешування запитів, що повторюються.
Але в чому ж його користь? Ви могли б помістити до класу замісника якусь проміжну логіку, що виконувалася б до або після викликів цих самих методів чинного об’єкта. А завдяки однаковому інтерфейсу об’єкт-замісник можна передати до будь-якого коду, що очікує на сервісний об’єкт.
Аналогія з життяПлатіжною карткою можна розраховуватися так само, як і готівкою.
Платіжна картка — це замісник пачки готівки. І чек, і готівка мають спільний інтерфейс — ними обома можна оплачувати товари. Вигода покупця в тому, що не потрібно носити з собою «тонни» готівки. З іншого боку власник магазину не змушений замовляти клопітку інкасацію коштів з магазину, бо вони потрапляють безпосередньо на його банківський рахунок.
СтруктураІнтерфейс сервісу визначає загальний інтерфейс для сервісу й замісника. Завдяки цьому об’єкт замісника можна використовувати там, де очікується об’єкт сервісу.
Сервіс містить корисну бізнес-логіку.
Замісник зберігає посилання на об’єкт сервісу. Після того, як замісник закінчує свою роботу (наприклад, ініціалізацію, логування, захист або інше), він передає виклики вкладеному сервісу.
Замісник може сам відповідати за створення й видалення об’єкта сервісу.
Клієнт працює з об’єктами через інтерфейс сервісу. Завдяки цьому його можна «обдурити», підмінивши об’єкт сервісу об’єктом замісника.
ПсевдокодУ цьому прикладі Замісник допомагає додати до програми механізм ледачої ініціалізації та кешування результатів роботи бібліотеки інтеграції з YouTube.
Приклад кешування результатів роботи реального сервісу за допомогою замісника.
Оригінальний об’єкт починав завантаження з мережі, навіть якщо користувач повторно запитував одне й те саме відео. Замісник завантажує відео тільки один раз, використовуючи для цього службовий об’єкт, але в інших випадках повертає закешований файл.
// Інтерфейс віддаленого сервісу.interface ThirdPartyYouTubeLib is
method listVideos()
method getVideoInfo(id)
method downloadVideo(id)
// Конкретна реалізація сервісу. Методи цього класу запитують у
// YouTube різну інформацію. Швидкість запиту залежить не лише
// від якості інтернет-каналу користувача, але й від стану
// самого YouTube. Тому, чим більше буде викликів до сервісу,
// тим менш відзивною стане програма.
class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib is
method listVideos() is
// Отримати список відеороликів за допомогою API
// YouTube.
method getVideoInfo(id) is
// Отримати детальну інформацію про якийсь відеоролик.
method downloadVideo(id) is
// Завантажити відео з YouTube.
// З іншого боку, можна кешувати запити до YouTube і не
// повторювати їх деякий час, доки кеш не застаріє. Але внести
// цей код безпосередньо в сервісний клас неможливо, бо він
// знаходиться у сторонній бібліотеці. Тому ми помістимо логіку
// кешування в окремий клас-обгортку. Він буде делегувати запити
// сервісному об'єкту, тільки якщо потрібно безпосередньо
// відіслати запит.
class CachedYouTubeClass implements ThirdPartyYouTubeLib is
private field service: ThirdPartyYouTubeLib
private field listCache, videoCache
field needReset
constructor CachedYouTubeClass(service: ThirdPartyYouTubeLib) is
this.service = service
method listVideos() is
if (listCache == null || needReset)
listCache = service.listVideos()
return listCache
method getVideoInfo(id) is
if (videoCache == null || needReset)
videoCache = service.getVideoInfo(id)
return videoCache
method downloadVideo(id) is
if (!downloadExists(id) || needReset)
service.downloadVideo(id)
// Клас GUI, який використовує сервісний об'єкт. Замість
// реального сервісу, ми підсунемо йому об'єкт-замісник. Клієнт
// нічого не помітить, так як замісник має той самий інтерфейс,
// що й сервіс.
class YouTubeManager is
protected field service: ThirdPartyYouTubeLib
constructor YouTubeManager(service: ThirdPartyYouTubeLib) is
this.service = service
method renderVideoPage(id
!Увага!
Сайт зберігає кукі вашого браузера. Ви зможете в будь-який момент зробити закладку та продовжити читання книги «Занурення в патерни проектування, Олександр Швець», після закриття браузера.