Метафора
Представьте, что нам требуется поставить пьесу. Однако по сценарию в этой пьесе задействованы несколько десятков людей, которые по своей сути выполняют одинаковые действия, например, участвуют в массовках различных сцен в разные промежутки времени, но между ними всё же есть какие-то различия (например, костюмы). Нам бы стоило огромных денег нанимать для каждой роли отдельного актера, поэтому мы используем паттерн «приспособленец». Мы создадим все нужные нам костюмы, но для каждой массовки будем переодевать небольшую группу актеров в требуемые для этой сцены костюмы. В результате мы имеем возможность ценой малых ресурсов создавать видимость управления большим количеством казалось бы разных объектов.
Назначение
Приспособленец — это экземпляр объекта, который выдаёт себя за группу самостоятельных экземпляров.
Используется, когда:
- В приложении используется большое число очень похожих друг на друга экземпляров заданного класса;
- Часть состояния объекта является контекстной и может быть легко вынесена во внешние структуры;
- После вынесения части состояния все объекты становятся одинаковыми и это даёт возможность заменить их одним;
В результате использования шаблона создаётся гораздо меньше экземпляров, что влечёт за собой экономию ресурсов.
При проектировании приспособленца необходимо разделить его свойства на внешние и внутренние. Внутренние свойства всегда неизменны, тогда как внешние могут отличаться в зависимости от места и контекста применения и должны быть вынесены за пределы приспособленца.
Приспособленцы моделируют сущности предметной области, число которых слишком велико для представления их реальными объектами.
Недостаток:
- При большом объеме разделяемых приспособленцев, увеличивается время на его поиск в пуле (FlyweightFactory).
Диаграмма
Существуют два типа приспособленцев:
- UnsharedConcreteFlyweight — объект, который не управляется через фабрику приспособленцев, а создаётся клиентом напрямую;
- ConcreteFlyweight — объект, доступ к которому клиент получает через фабрику приспособленцев FlyweightFactory по некоторому ID ключу.
Пример
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
*&---------------------------------------------------------------------* *& Определение классов шаблона *&---------------------------------------------------------------------* CLASS lcl_character DEFINITION ABSTRACT. PUBLIC SECTION. METHODS: display, constructor IMPORTING iv_character TYPE c. PROTECTED SECTION. DATA: mv_char TYPE c, mv_width TYPE i, mv_height TYPE i. ENDCLASS. CLASS lcl_character IMPLEMENTATION. METHOD display. WRITE: / `Char: `, mv_char, `,width: `, mv_width, `,height: `, mv_height. ENDMETHOD. METHOD constructor. mv_char = iv_character. ENDMETHOD. ENDCLASS. CLASS lcl_character_a DEFINITION INHERITING FROM lcl_character. PUBLIC SECTION. METHODS: constructor IMPORTING iv_character TYPE c. ENDCLASS. CLASS lcl_character_a IMPLEMENTATION. METHOD constructor. super->constructor( iv_character = iv_character ). mv_width = 100. mv_height = 100. ENDMETHOD. ENDCLASS. CLASS lcl_character_b DEFINITION INHERITING FROM lcl_character. PUBLIC SECTION. METHODS: constructor IMPORTING iv_character TYPE c. ENDCLASS. CLASS lcl_character_b IMPLEMENTATION. METHOD constructor. super->constructor( iv_character = iv_character ). mv_width = 200. mv_height = 200. ENDMETHOD. ENDCLASS. CLASS lcl_character_z DEFINITION INHERITING FROM lcl_character. PUBLIC SECTION. METHODS: constructor IMPORTING iv_character TYPE c. ENDCLASS. CLASS lcl_character_z IMPLEMENTATION. METHOD constructor. super->constructor( iv_character = iv_character ). mv_width = 300. mv_height = 300. ENDMETHOD. ENDCLASS. CLASS lcl_character_factory DEFINITION. PUBLIC SECTION. CLASS-METHODS: get_character IMPORTING iv_char TYPE c RETURNING VALUE(ro_character) TYPE REF TO lcl_character. PRIVATE SECTION. TYPES: BEGIN OF ty_char, char TYPE c LENGTH 1, ref TYPE REF TO lcl_character, END OF ty_char. CLASS-DATA: lt_characters TYPE STANDARD TABLE OF ty_char. ENDCLASS. CLASS lcl_character_factory IMPLEMENTATION. METHOD get_character. TRY. ro_character = lt_characters[ char = iv_char ]-ref. CATCH cx_sy_itab_line_not_found. APPEND INITIAL LINE TO lt_characters ASSIGNING FIELD-SYMBOL(<ls_line>). <ls_line>-char = iv_char. CASE <ls_line>-char. WHEN 'A'. <ls_line>-ref = NEW lcl_character_a( iv_char ). WHEN 'B'. <ls_line>-ref = NEW lcl_character_b( iv_char ). ... WHEN 'Z'. <ls_line>-ref = NEW lcl_character_z( iv_char ). ENDCASE. ro_character = <ls_line>-ref. ENDTRY. ENDMETHOD. ENDCLASS. *&---------------------------------------------------------------------* *& Работа с шаблоном *&---------------------------------------------------------------------* START-OF-SELECTION. DATA: lv_message TYPE string VALUE 'AZABAZZZAAABBB', lo_character TYPE REF TO lcl_character. DO strlen( lv_message ) - 1 TIMES. lo_character = lcl_character_factory=>get_character( iv_char = substring( val = lv_message off = sy-index len = 1 ) ). lo_character->display( ). ENDDO. |
При анализе строки мы бы могли каждую букву представить в виде отдельного объекта, однако при увеличении строки, у нас бы росло и число создаваемых объектов, что могло бы негативно отразиться на потребляемой памяти. Вместо этого мы использовали шаблон приспособленец, в итоге кол-во объектов ограничено словарём. В примере используются только управляемые (создаваемые) фабрикой приспособленцы.
Одним из примеров применения в ABAP — это использование шаблона для обработки внутренних таблиц, где каждая строка может быть представлена приспособленцем и логика обработки полей строки заложена в его методах.
Интересно