Factory Pattern with Singleton Pattern
I will share with you how am using the factory and singleton pattern.
I found it more useful to use singleton pattern and factory pattern together. The factory pattern will control which instance of an object are single instances and which not.
How the code works:
- The program requests the factory class for an object, supplying the object name.
- The factory class check in the internal table l_tbl_objects if an instance of the object with the same name is already created.
- If the object exists in the internal the system return the instance of that object.
- If the object does not exists the system creates a new instance of the object and stores it into the l_tbl_objects table.
For creating singleton objects we will store the instance of the object in an internal table (we assume a single object exist within a user session):
begin of L_SC_OBJECTS,
object_name type string,
object type REF TO Z_OBJECT,
end of L_SC_OBJECTS.
Data: l_tbl_objects type STANDARD TABLE of L_SC_OBJECTS.
The factory class would be a static object:
CLASS z_factory DEFINITION.
PUBLIC SECTION.
CLASS-METHODS:
get_counter
IMPORTING
i_v_counter_type TYPE string
RETURNING
value(r_v_object) TYPE REF TO object
.
PRIVATE SECTION.
CLASS-DATA:
l_tbl_objects TYPE STANDARD TABLE OF l_sc_objects.
CLASS-METHODS:
_createobject
IMPORTING
i_v_class TYPE STRING
RETURNING
value(r_o_object) TYPE REF TO object,
_objectexists
IMPORTING
i_v_objectname TYPE string
RETURNING
value(r_v_exist) TYPE i,
_object
IMPORTING
i_v_objectname TYPE string
RETURNING
value(r_v_object) TYPE ref to object ,
_add_object
IMPORTING
i_O_object TYPE REF TO object
i_v_objectname TYPE string
.
ENDCLASS. "z_factory DEFINITION
For creating objects dynamically you use (code I borrow from CL_EXITHANDLER)
CONCATENATE '\CLASS=' i_v_class INTO l_v_classname.
CREATE OBJECT l_o_object TYPE (l_v_classname).
However in the example below the object is a local class therefore we need to include the Program name ahead of the the \class
CONCATENATE '\PROGRAM=!MY_TEST1\CLASS=' i_v_class INTO l_v_classname.
The factory implementation
LASS z_factory IMPLEMENTATION.
METHOD get_counter.
DATA: l_v_objectname TYPE string,
l_o_counter TYPE REF TO object,
l_v_return TYPE i,
l_o_obj TYPE REF TO object
.
MOVE i_v_counter_type TO l_v_objectname .
CALL METHOD z_factory=>_objectexists
EXPORTING
i_v_objectname = l_v_objectname
RECEIVING
r_v_exist = l_v_return.
IF l_v_return EQ 0.
CALL METHOD z_factory=>_object
EXPORTING
i_v_objectname = l_v_objectname
RECEIVING
r_v_object = l_o_obj.
MOVE l_o_obj ?TO r_v_object.
RETURN.
ENDIF.
"CREATE OBJECT l_o_counter.
l_o_counter ?= z_factory=>_createobject( i_v_class = 'Z_COUNTER' ).
MOVE l_o_counter TO l_o_obj.
CALL METHOD z_factory=>_add_object
EXPORTING
i_o_object = l_o_obj
i_v_objectname = l_v_objectname.
r_v_object = l_o_counter.
ENDMETHOD. "Get_Counter
METHOD _createobject.
DATA: l_o_object TYPE REF TO object,
l_v_classname TYPE string
.
CONCATENATE '\PROGRAM=!CH_TEST1\CLASS=' i_v_class INTO l_v_classname.
CREATE OBJECT l_o_object TYPE (l_v_classname).
MOVE l_o_object TO r_o_object.
ENDMETHOD. "_createobject
METHOD _objectexists.
READ TABLE l_tbl_objects WITH KEY object_name = i_v_objectname TRANSPORTING NO FIELDS.
IF sy-subrc EQ 0.
r_v_exist = 0.
ELSE.
r_v_exist = -1.
ENDIF.
ENDMETHOD. "_OBJECTEXISTS
METHOD _object.
DATA: l_wa_objects TYPE l_sc_objects.
READ TABLE l_tbl_objects WITH KEY object_name = i_v_objectname
INTO l_wa_objects.
IF sy-subrc EQ 0.
MOVE l_wa_objects-object TO r_v_object.
ENDIF.
ENDMETHOD. "_OBJECT
METHOD _add_object.
DATA: l_wa_cast TYPE l_sc_objects.
FIELD-SYMBOLS: <f_sec> TYPE l_sc_objects.
l_wa_cast-object_name = i_v_objectname.
l_wa_cast-object = i_o_object.
READ TABLE l_tbl_objects ASSIGNING <f_sec> WITH KEY object_name = l_wa_cast-object_name.
IF sy-subrc EQ 0.
<f_sec>-object = i_o_object.
ELSE.
APPEND l_wa_cast TO l_tbl_objects.
ENDIF.
ENDMETHOD. "_add_object
ENDCLASS. "z_Factory IMPLEMENTATION
Test scenario
We create a test object
CLASS z_counter DEFINITION.
PUBLIC SECTION.
METHODS:
contructor,
add_value,
counter
RETURNING value(r_v_amount) TYPE i.
PRIVATE SECTION.
DATA: l_v_amount TYPE i.
ENDCLASS. "z_COUNTER DEFINITION
*----------------------------------------------------------------------*
* CLASS z_COUNTER IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS z_counter IMPLEMENTATION.
METHOD contructor.
l_v_amount = 0.
ENDMETHOD. "CONTRUCTOR
METHOD add_value.
l_v_amount = l_v_amount + 1.
ENDMETHOD. "ADD_VALUE
METHOD counter.
r_v_amount = l_v_amount.
ENDMETHOD. "COUNTER
ENDCLASS. "z_COUNTER IMPLEMENTATION
Define 2 instances of the object
DATA:
l_o_counter1 TYPE REF TO z_counter ,
l_o_counter2 TYPE REF TO z_counter ,
l_v_i TYPE i ,
l_v_i2 TYPE i
.
Create the objects
l_o_counter1 ?= z_factory=>get_counter('CARS').
l_v_i = l_o_counter1->counter( ).
l_o_counter2 ?= z_factory=>get_counter('CARS').
l_o_counter2->add_value( ).
l_o_counter2->add_value( ).
WRITE l_v_i.
l_v_i = l_o_counter1->counter( ).
l_v_i2 = l_o_counter2->counter( ).
WRITE: l_v_i, l_v_i2.