Метафора
Наверняка вы работали когда-нибудь с прокси-серверами? Суть паттерна такая же, есть некоторый прокси объект, который делает перенаправление вызовов к реальному объекту. При этом для Вас как для клиента ничего не меняется, интерфейс остаётся тем же.
Назначение
Является суррогатом другого объекта и контролирует доступ к нему. Решает проблему контроля доступа к объекту, не изменяя при этом клиента.
Паттерн заместитель очень сильно напоминает другой шаблон с похожим назначением – «декоратор». Однако у них разные цели, заместитель в отличие от декоратора не должен изменять результаты операции реального объекта. Кроме того, заместитель определяет статическую связь заместителя и объекта, когда как декоратор определяет это динамически.
Классы заместители применяются там, где нужно спрятать исходный объект и добавить к его методам некоторое поведение (не изменять результат, например, кеширование), позволить отложить создание дорогостоящего объекта, контролировать количество вызовов метода или спрятать удаленную природу объекта.
Диаграмма
Пример
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
*&---------------------------------------------------------------------* *& Определение классов шаблона *&---------------------------------------------------------------------* CLASS lcl_subject DEFINITION ABSTRACT. PUBLIC SECTION. METHODS: get_data ABSTRACT RETURNING VALUE(rt_spfli) TYPE spfli_tab. ENDCLASS. CLASS lcl_real_subject DEFINITION INHERITING FROM lcl_subject. PUBLIC SECTION. METHODS: get_data REDEFINITION, constructor. PRIVATE SECTION. DATA: mt_data TYPE spfli_tab. ENDCLASS. CLASS lcl_real_subject IMPLEMENTATION. METHOD constructor. super->constructor( ). " чтение данных в конструкторе, не самое лучшее решение.. SELECT * FROM spfli INTO CORRESPONDING FIELDS OF TABLE mt_data. ENDMETHOD. METHOD get_data. rt_spfli = mt_data. ENDMETHOD. ENDCLASS. CLASS lcl_proxy DEFINITION INHERITING FROM lcl_subject. PUBLIC SECTION. METHODS: get_data REDEFINITION. PRIVATE SECTION. DATA: mo_real_subject TYPE REF TO lcl_real_subject. ENDCLASS. CLASS lcl_proxy IMPLEMENTATION. METHOD get_data. " Инициализция будет происходить только при необходимости получить данные, " а не во время инициализации IF mo_real_subject IS NOT BOUND. CREATE OBJECT mo_real_subject. ENDIF. rt_spfli = mo_real_subject->get_data( ). ENDMETHOD. ENDCLASS. *&---------------------------------------------------------------------* *& Работа с шаблоном *&---------------------------------------------------------------------* START-OF-SELECTION. DATA: lo_subject TYPE REF TO lcl_subject. CREATE OBJECT lo_subject TYPE lcl_proxy. DATA(lt_data) = lo_subject->get_data( ). |
В примере вместо использования реального объекта, мы воспользуемся прокси, который отложит инициализацию (чтение) данных на момент, когда они нам реально потребуется, а не при создании объекта.
Указано, что
» чтение данных в конструкторе, не самое лучшее решение..
SELECT * FROM spfli INTO CORRESPONDING FIELDS OF TABLE mt_data.»
К примеру, необходимо выбрать номер рейса, дату из таблицы и присвоить их переменным класса в конструкторе.
Не подскажите тогда, как можно заменить данное не самое лучшее решение?
Именно этим и занимается прокси в примере, решает проблему перенося запрос данных на более поздний срок. Ну а при проектировании своих объектов отложенная инициализация является неплохим подходом. https://ru.m.wikipedia.org/wiki/Отложенная_инициализация
Если абстрагироваться от прокси, то, мое личное мнение, инициализация данных в конструкторе вполне нормальная ситуация, если эти данные необходимы для полноты объекта.
Пример: класс, описывающий какой-либо документ, который отвечает лишь за получение и обработку данных по нему. Смысл существования такого объекта с ключевыми полями в качестве атрибута, но без основных данных из БД, равен нулю. И в любом случае после создания такого объекта нам придется вызывать его инициализацию…
Отложенная инициализация имеет место быть, но опять же для ряда случаев. Например, тот же класс (описанный выше) получает доп. функционал в виде удаления записи из БД. В таком случае необязательно получать данные из БД по ключам, однако необходимо в конструкторе выполнить проверку, что такая запись вообще существует. В случае отсутствия — кинуть исключение прямо из конструктора.
Кстати, я бы в качестве примера все же сделал lcl_subject интерфейсом, а прокси и реальный объект — его реализацией. Смысла в абстрактном классе нет.
К тому же, часто приходится именно навешивать прокси, когда уже есть готовый реальный объект, но к нему нужно что-то еще, какой-то доп.функционал.
Я бы все же для примера взял задачу, когда над вызовом каждого метода реального объекта необходимо добавить запись этого действия в лог. В таком случае это будет реальный пример из жизни, максимально описывающий смысл прокси.
Вот в этом видео у Немчинского тема прокси отлично раскрыта: https://youtu.be/gCLePmhzy9o?list=PLmqFxxywkatStbd9hdzVOS1hZa9dc56k4