In this blog, see how the prototype design pattern is used for item cloning in an ABAP system.
Item Clone
In this example, we will work on an imaginary purchase requisition application where the user has to enter the vendor code (LIFNR), material number (MATNR), unit price (PREIS and WAERS), and quantity (MENGE and MEINS) into the GUI. We will want it to be possible to enter multiple line items.
Below demonstrates what the model class of such an application would roughly look like.
CLASS zcl_purchase DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
“ Some type definitions
METHODS add_line IMPORTING !is_line TYPE t_line.
METHODS load_from_db IMPORTING !iv_docno TYPE zdocno.
METHODS save_to_db.
PRIVATE SECTION.
“ Some hidden variables
“ Some boring methods
PROTECTED SECTION.
ENDCLASS.
CLASS zcl_purchase IMPLEMENTATION.
METHOD add_line.
APPEND VALUE #(
obj_material = NEW zcl_material( is_line-matnr )
lifnr = is_line-lifnr
matnr = is_line-matnr
price = VALUE #(
preis = is_line-preis
waers = is_line-waers )
quan = VALUE #(
menge = is_line-menge
meins = is_line-meins )
) TO gt_line.
ENDMETHOD.
METHOD load_from_db.
“ Some boring code containing SELECT, etc.
ENDMETHOD.
METHOD save_to_db.
“ Some boring code containing MODIFY, etc.
ENDMETHOD.
ENDCLASS.
So far, so good. As you see, GT_LINE is a nested internal table with five fields: one object (OBJ_MATERIAL); two variables (LIFNR, MATNR); and two work areas (PRICE, QUAN). At this point, we will assume that creating a new CL_MATERIAL has a high runtime cost, which means that CL_MATERIAL~CONSTRUCTOR contains costly code snippets that take quite some time to execute.
Below demonstrates what the CL_MATERIAL class looks like.
CLASS zcl_material DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
“ Some type definitions
METHODS constructor IMPORTING !iv_matnr TYPE matnr.
“ Some further methods
PRIVATE SECTION.
“ Some hidden methods
PROTECTED SECTION.
ENDCLASS.
CLASS zcl_material IMPLEMENTATION.
METHOD constructor.
“ Some very slow but unavoidable code
ENDMETHOD.
“ Some more methods
ENDCLASS.
If the user enters a new line item containing a brand new material, we must call ADD_LINE again. Thus, because we will be creating a new instance of CL_MATERIAL we will have to endure high runtime cost. On the other hand, what if the user enters a material number that he/she had entered before? Would we still create a brand new instance of CL_MATERIAL? With our current code, we would. So we must ask ourselves, “Is there a better way?”
Luckily there is! What we will do is clone an existing material object instead of creating a new one—the core logic of the prototype design pattern. Let’s look at the Unified Modeling Language (UML) diagram below to see what this should look like.
Item Clone
In this example, we will work on an imaginary purchase requisition application where the user has to enter the vendor code (LIFNR), material number (MATNR), unit price (PREIS and WAERS), and quantity (MENGE and MEINS) into the GUI. We will want it to be possible to enter multiple line items.
Below demonstrates what the model class of such an application would roughly look like.
CLASS zcl_purchase DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
“ Some type definitions
METHODS add_line IMPORTING !is_line TYPE t_line.
METHODS load_from_db IMPORTING !iv_docno TYPE zdocno.
METHODS save_to_db.
PRIVATE SECTION.
“ Some hidden variables
“ Some boring methods
PROTECTED SECTION.
ENDCLASS.
CLASS zcl_purchase IMPLEMENTATION.
METHOD add_line.
APPEND VALUE #(
obj_material = NEW zcl_material( is_line-matnr )
lifnr = is_line-lifnr
matnr = is_line-matnr
price = VALUE #(
preis = is_line-preis
waers = is_line-waers )
quan = VALUE #(
menge = is_line-menge
meins = is_line-meins )
) TO gt_line.
ENDMETHOD.
METHOD load_from_db.
“ Some boring code containing SELECT, etc.
ENDMETHOD.
METHOD save_to_db.
“ Some boring code containing MODIFY, etc.
ENDMETHOD.
ENDCLASS.
So far, so good. As you see, GT_LINE is a nested internal table with five fields: one object (OBJ_MATERIAL); two variables (LIFNR, MATNR); and two work areas (PRICE, QUAN). At this point, we will assume that creating a new CL_MATERIAL has a high runtime cost, which means that CL_MATERIAL~CONSTRUCTOR contains costly code snippets that take quite some time to execute.
Below demonstrates what the CL_MATERIAL class looks like.
CLASS zcl_material DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
“ Some type definitions
METHODS constructor IMPORTING !iv_matnr TYPE matnr.
“ Some further methods
PRIVATE SECTION.
“ Some hidden methods
PROTECTED SECTION.
ENDCLASS.
CLASS zcl_material IMPLEMENTATION.
METHOD constructor.
“ Some very slow but unavoidable code
ENDMETHOD.
“ Some more methods
ENDCLASS.
If the user enters a new line item containing a brand new material, we must call ADD_LINE again. Thus, because we will be creating a new instance of CL_MATERIAL we will have to endure high runtime cost. On the other hand, what if the user enters a material number that he/she had entered before? Would we still create a brand new instance of CL_MATERIAL? With our current code, we would. So we must ask ourselves, “Is there a better way?”
Luckily there is! What we will do is clone an existing material object instead of creating a new one—the core logic of the prototype design pattern. Let’s look at the Unified Modeling Language (UML) diagram below to see what this should look like.
The UML diagram has only one extra method: CLONE. This method will do exactly what it says: clone the object. That’s the easy part; the relatively tricky part will transpire in CL_PURCHASE. Let’s start simple and see what CL_MATERIAL looks like below.
CLASS zcl_material DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
“ Some type definitions
METHODS constructor IMPORTING !iv_matnr TYPE matnr.
METHODS clone RETURNING VALUE(ro_material) TYPE REF TO zcl_material.
“ Some further methods
PRIVATE SECTION.
“ Some hidden methods
PROTECTED SECTION.
ENDCLASS.
CLASS zcl_material IMPLEMENTATION.
METHOD constructor.
“ Some very slow but unavoidable code
ENDMETHOD.
METHOD clone.
SYSTEM-CALL OBJMGR CLONE me TO ro_material.
ENDMETHOD.
“ Some more methods
ENDCLASS.
Normally, within the CLONE method, we would create the object RO_MATERIAL (avoiding the slow code in the constructor using flag variables and conditions) and copy the entire state of ME to RO_MATERIAL. However, we used a cheat code. What you see is a system call to clone ME, which is not meant for external use. No one gets hurt by little hacks like this, as long as the unit tests are done well.
Now, back to CL_PURCHASE. Instead of creating a new CL_MATERIAL object every time ADD_LINE is called, we will clone an existing material object whenever possible. The code to do so can be seen below.
METHOD add_line.
DATA lo_material TYPE REF TO zcl_material.
ASSIGN gt_line[ matnr = is_line-matnr ]
TO FIELD-SYMBOL(<ls_line>).
IF sy-subrc EQ 0.
lo_material = <ls_line>-obj_material->clone( ).
ELSE.
lo_material = NEW zcl_material( is_line-matnr ).
ENDIF.
APPEND VALUE #(
obj_material = lo_material
lifnr = is_line-lifnr
matnr = is_line-matnr
price = VALUE #(
preis = is_line-preis
waers = is_line-waers )
quan = VALUE #(
menge = is_line-menge
meins = is_line-meins )
) TO gt_line.
ENDMETHOD.
Brilliant, isn’t it? Instead of re-calling the constructor every time the same material is added, we simply cloned an existing object and avoided the initialization runtime cost.
No comments:
Post a Comment