Замісник
Суть патерна
Замісник — це структурний патерн проектування, що дає змогу підставляти замість реальних об’єктів спеціальні об’єкти-замінники. Ці об’єкти перехоплюють виклики до оригінального об’єкта, дозволяючи зробити щось до чи після передачі виклику оригіналові.
Проблема
Для чого взагалі контролювати доступ до об’єктів? Розглянемо такий приклад: у вас є зовнішній ресурсоємний об’єкт, який потрібен не весь час, а лише зрідка.
Ми могли б створювати цей об’єкт не на самому початку програми, а тільки тоді, коли він реально кому-небудь знадобиться. Кожен клієнт об’єкта отримав би деякий код відкладеної ініціалізації. Це, ймовірно, призвело б до дублювання великої кількості коду.
В ідеалі цей код хотілося б помістити безпосередньо до службового класу, але це не завжди можливо. Наприклад, код класу може знаходитися в закритій сторонній бібліотеці.
Рішення
Патерн Замісник пропонує створити новий клас-дублер, який має той самий інтерфейс, що й оригінальний службовий об’єкт. При отриманні запиту від клієнта об’єкт-замісник сам би створював примірник службового об’єкта та переадресовував би йому всю реальну роботу.
Але в чому ж його користь? Ви могли б помістити до класу замісника якусь проміжну логіку, що виконувалася б до або після викликів цих самих методів чинного об’єкта. А завдяки однаковому інтерфейсу об’єкт-замісник можна передати до будь-якого коду, що очікує на сервісний об’єкт.
Аналогія з життя
Платіжна картка — це замісник пачки готівки. І чек, і готівка мають спільний інтерфейс — ними обома можна оплачувати товари. Вигода покупця в тому, що не потрібно носити з собою «тонни» готівки. З іншого боку власник магазину не змушений замовляти клопітку інкасацію коштів з магазину, бо вони потрапляють безпосередньо на його банківський рахунок.
Структура
-
Інтерфейс сервісу визначає загальний інтерфейс для сервісу й замісника. Завдяки цьому об’єкт замісника можна використовувати там, де очікується об’єкт сервісу.
-
Сервіс містить корисну бізнес-логіку.
-
Замісник зберігає посилання на об’єкт сервісу. Після того, як замісник закінчує свою роботу (наприклад, ініціалізацію, логування, захист або інше), він передає виклики вкладеному сервісу.
Замісник може сам відповідати за створення й видалення об’єкта сервісу.
-
Клієнт працює з об’єктами через інтерфейс сервісу. Завдяки цьому його можна «обдурити», підмінивши об’єкт сервісу об’єктом замісника.
Псевдокод
У цьому прикладі Замісник допомагає додати до програми механізм ледачої ініціалізації та кешування результатів роботи бібліотеки інтеграції з YouTube.
Оригінальний об’єкт починав завантаження з мережі, навіть якщо користувач повторно запитував одне й те саме відео. Замісник завантажує відео тільки один раз, використовуючи для цього службовий об’єкт, але в інших випадках повертає закешований файл.
Застосування
Лінива ініціалізація (віртуальний проксі). Коли у вас є важкий об’єкт, який завантажує дані з файлової системи або бази даних.
Замість того, щоб завантажувати дані відразу після старту програми, можна заощадити ресурси й створити об’єкт тоді, коли він дійсно знадобиться.
Захист доступу (захищаючий проксі). Коли в програмі є різні типи користувачів, і вам хочеться захистити об’єкт від неавторизованого доступу. Наприклад, якщо ваші об’єкти — це важлива частина операційної системи, а користувачі — сторонні програми (корисні чи шкідливі).
Проксі може перевіряти доступ під час кожного виклику та передавати виконання службовому об’єкту, якщо доступ дозволено.
Локальний запуск сервісу (віддалений проксі). Коли справжній сервісний об’єкт знаходиться на віддаленому сервері.
У цьому випадку замісник транслює запити клієнта у виклики через мережу по протоколу, який є зрозумілим віддаленому сервісу.
Логування запитів (логуючий проксі). Коли потрібно зберігати історію звернень до сервісного об’єкта.
Замісник може зберігати історію звернення клієнта до сервісного об’єкта.
Кешування об’єктів («розумне» посилання). Коли потрібно кешувати результати запитів клієнтів і керувати їхнім життєвим циклом.
Замісник може підраховувати кількість посилань на сервісний об’єкт, які були віддані клієнту та залишаються активними. Коли всі посилання звільняться, можна буде звільнити і сам сервісний об’єкт (наприклад, закрити підключення до бази даних).
Крім того, Замісник може відстежувати, чи клієнт не змінював сервісний об’єкт. Це дозволить повторно використовувати об’єкти й суттєво заощаджувати ресурси, особливо якщо мова йде про великі «ненажерливі» сервіси.
Кроки реалізації
-
Визначте інтерфейс, який би зробив замісника та оригінальний об’єкт взаємозамінними.
-
Створіть клас замісника. Він повинен містити посилання на сервісний об’єкт. Частіше за все сервісний об’єкт створюється самим замісником. У рідкісних випадках замісник отримує готовий сервісний об’єкт від клієнта через конструктор.
-
Реалізуйте методи замісника в залежності від його призначення. У більшості випадків, виконавши якусь корисну роботу, методи замісника повинні передати запит сервісному об’єкту.
-
Подумайте про введення фабрики, яка б вирішувала, який з об’єктів створювати: замісника або реальний сервісний об’єкт. Проте, з іншого боку, ця логіка може бути вкладена до створюючого методу самого замісника.
-
Подумайте, чи не реалізувати вам ліниву ініціалізацію сервісного об’єкта при першому зверненні клієнта до методів замісника.
Переваги та недоліки
- Дозволяє контролювати сервісний об’єкт непомітно для клієнта.
- Може працювати, навіть якщо сервісний об’єкт ще не створено.
- Може контролювати життєвий цикл службового об’єкта.
- Ускладнює код програми внаслідок введення додаткових класів.
- Збільшує час отримання відклику від сервісу.
Відносини з іншими патернами
-
З Адаптером ви отримуєте доступ до існуючого об’єкта через інший інтерфейс. Використовуючи Замісник, інтерфейс залишається незмінним. Використовуючи Декоратор, ви отримуєте доступ до об’єкта через розширений інтерфейс.
-
Фасад схожий на Замісник тим, що замінює складну підсистему та може сам її ініціалізувати. Але, на відміну від Фасаду, Замісник має такий самий інтерфейс, що і його службовий об’єкт, завдяки чому їх можна взаємозаміняти.
-
Декоратор та Замісник мають схожі структури, але різні призначення. Вони схожі тим, що обидва побудовані на композиції та делегуванні роботи іншому об’єкту. Патерни відрізняються тим, що Замісник сам керує життям сервісного об’єкта, а обгортання Декораторів контролюється клієнтом.