Метафора
Данный паттерн чем-то напоминает «фабрику», он также служит для создания объектов, однако с немного другим подходом. Представьте, что у вас есть пустой пакет (из-под сока), а вам нужен полный с апельсиновым соком. Вы «говорите» пакету «Хочу пакет апельсинового сока», он в свою очередь создает свою копию и заполняет ее соком, который вы попросили. Немного «сказочный пример», но в программировании часто так и бывает. В данном случае пустой пакет и является «прототипом», и в зависимости от того что вам требуется, он создает на своей основе требуемые вами объекты (пакеты сока). Клонирование не обязательно должно производится на самом «пакете», это может быть и какой-то другой «объект», главное лишь что данный «прототип» позволяет получать его экземпляры.
Назначение
Паттерн применяется для тех случаев, когда требуется построение некоторых объектов на базе состояния общего для них прототипа. Клонирование в данном контексте подразумевает копирование состояния одного объекта, и установка его в другом. Сам процесс построения клонов не подразумевает повторный вызов конструктора у клонов на стороне клиента, в данном случае используется вызов специального метода, который вернет клона с состоянием как у прототипа. Благодаря тому, что метод клонирования наследуется из базового класса, клиенту не обязательно знать к какому типу относится объект.
Часто паттерн применяется, когда первоначальное состояние объекта довольно долго инициализируется, при этом данный шаг пропускается при операции клонирования, т.к. прототип уже проинициализирован и может предоставить своё состояние для клона.
Диаграмма
Пример
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 |
*&---------------------------------------------------------------------* *& Определение классов *&---------------------------------------------------------------------* INTERFACE lif_clonable. METHODS: clone RETURNING VALUE(ro_clone) TYPE REF TO lif_clonable. ENDINTERFACE. CLASS lcl_request DEFINITION. PUBLIC SECTION. INTERFACES: lif_clonable. ALIASES clone FOR lif_clonable~clone. TYPES: BEGIN OF ty_request_state, request_id TYPE i, entity_id TYPE i, request_data TYPE string, END OF ty_request_state. METHODS: constructor IMPORTING iv_entity_id TYPE i is_state TYPE ty_request_state OPTIONAL, get_state RETURNING VALUE(rs_state) TYPE ty_request_state. PRIVATE SECTION. DATA: ms_request_data TYPE ty_request_state. ENDCLASS. CLASS lcl_request IMPLEMENTATION. METHOD constructor. " Если передали состояние, инициализируемся по нему IF is_state IS SUPPLIED. ms_request_data = is_state. RETURN. ENDIF. " Эмулируем вызов RFC WAIT UP TO 3 SECONDS. " В реальности было бы что-то вроде " CALL FUNCTION 'ZGET_DATA_FROM_RFC' " EXPORTING " iv_entity_id = iv_entity_id " IMPORTING " es_request_data = ms_request_data ms_request_data -entity_id = iv_entity_id. ms_request_data -request_data = 'Sample data'. ms_request_data -request_id = 1. ENDMETHOD. METHOD get_state. rs_state = ms_request_data . ENDMETHOD. METHOD clone. DATA: lo_clone TYPE REF TO lcl_request. CREATE OBJECT lo_clone EXPORTING iv_entity_id = 0 is_state = me->get_state( ). ro_clone = lo_clone. ENDMETHOD. ENDCLASS. *&---------------------------------------------------------------------* *& Работа с прототипом *&---------------------------------------------------------------------* START-OF-SELECTION. DATA: lo_request TYPE REF TO lcl_request, ls_state TYPE lcl_request=>ty_request_state, lo_clone TYPE REF TO lcl_request. CREATE OBJECT lo_request EXPORTING iv_entity_id = 1. ls_state = lo_request->get_state( ). WRITE: / ls_state-entity_id, ls_state-request_data, ls_state-request_id. " Повторный вызов конструктора приведет к задержке при инициализации " воспользуемся методом клонирования lo_clone ?= lo_request->clone( ). ls_state = lo_clone->get_state( ). WRITE: / ls_state-entity_id, ls_state-request_data, ls_state-request_id. |
В примере вместо повторной инициализации объекта используется метод его клонирования с передачей состояния. Для упрощения инициализация происходит прямо в конструкторе (запрос лучше отправлять в отдельном методе, а конструктор оставить только для присвоения состояния).
В других языках программирования есть встроенные механизмы для копирования объектов, в ABAP такой механизм так же имеется, реализован через системный вызов (см. класс cl_os_state):
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 |
*&---------------------------------------------------------------------* *& Определение классов *&---------------------------------------------------------------------* CLASS lcl_request DEFINITION INHERITING FROM cl_os_state. PUBLIC SECTION. ALIASES: clone FOR if_os_clone~clone. TYPES: BEGIN OF ty_request_state, request_id TYPE i, entity_id TYPE i, request_data TYPE string, END OF ty_request_state. METHODS: constructor IMPORTING iv_entity_id TYPE i iv_skip_init TYPE abap_bool OPTIONAL, get_state RETURNING VALUE(rs_state) TYPE ty_request_state. PRIVATE SECTION. DATA: ms_request_data TYPE ty_request_state. ENDCLASS. CLASS lcl_request IMPLEMENTATION. METHOD constructor. super->constructor( ). CHECK iv_skip_init = abap_false. " Эмулируем вызов RFC WAIT UP TO 3 SECONDS. " В реальности было бы что-то вроде " CALL FUNCTION 'ZGET_DATA_FROM_RFC' " EXPORTING " iv_entity_id = iv_entity_id " IMPORTING " es_request_data = ms_request_data ms_request_data-entity_id = iv_entity_id. ms_request_data-request_data = 'Sample data'. ms_request_data-request_id = 1. ENDMETHOD. METHOD get_state. rs_state = ms_request_data. ENDMETHOD. ENDCLASS. *&---------------------------------------------------------------------* *& Работа с прототипом *&---------------------------------------------------------------------* START-OF-SELECTION. DATA: lo_request TYPE REF TO lcl_request, ls_state TYPE lcl_request=>ty_request_state, lo_clone TYPE REF TO lcl_request. CREATE OBJECT lo_request EXPORTING iv_entity_id = 1. ls_state = lo_request->get_state( ). WRITE: / ls_state-entity_id, ls_state-request_data, ls_state-request_id. " Повторный вызов конструктора приведет к задержке при инициализации " воспользуемся методом клонирования lo_clone ?= lo_request->clone( ). ls_state = lo_clone->get_state( ). WRITE: / ls_state-entity_id, ls_state-request_data, ls_state-request_id. |