Keyword
Screen programming with ABAP OO, singleton pattern, Adapter pattern, polymorphism via interfaces.
The scenario
A company that has branches in different countries wants to implement a Dialog Application common for all these branches. The Screen should display the working hours for a specific country and an ALV grid should display some data.
The application takes as an input the company code by the user. Then instantiate the business object. The business class also instantiate 2 locals classes, which implement both of them the same interface. Based on the company code the business class could decide from which class will receive the localized data (each country have different laws and procedures). For this purpose I am using inside the class the technique “Polymorphism via interface”.
For simplistic reason I have remove the complexity from the business process and I have pay special attention to the control concepts.
Implementation Strategy
Following the OO concepts and the “Singleton” and the "adapter" patterns we should:
- Separate the Business Logic from the Programs’ Flow Logic.
- All the “functions” of the application should be wrapping into classes.
- A business object that represents the application should be able to instantiate only once and not 2 times.
- Screen flow logic should be controlled by methods
- When the localize classes (the classes that describe the business process of each country) are instantiating, then an event should be raised. That event should be handled by the main class, so later to be able to know with witch class should interact.
The application makes use of the following classes and interfaces.
Class / Interface Name | function | Type |
Lcl_app | Model class - Wrap all the business function | Class (create private) |
Lcl_local_gr | Localize class for Greece. Perform all the business calculations | Class – implement “if_localazation” interface |
Lcl_local_ch | Localize class for Switzerland. Perform all the business calculations | Class – implement “if_localazation” interface |
if_localazation | Common methods name and variable | interface |
- We are bringing into life the model/application class
- We are creating the screen objects (container, alv grid, fieldcatalogs, etc)
- We are creating the local business classes, that hold the business processes per country
- When a business class is created we are raising an interface event and the handler method is appending the instance into a table of instances.
Screen - Flow Logic
Screen 100 – Flow Logic |
process before output. module pbo. * process after input. module exit_command at exit-command. module pai.
|
Application Code
The main program |
*&---------------------------------------------------------------------* *& Report ZBC_GMICH_04 *& *&---------------------------------------------------------------------* *& *& *&---------------------------------------------------------------------*
report zbc_gmich_04.
include zbc_gmich_04_data. include zbc_gmich_04_class. include zbc_gmich_04_modules. *include zbc_gmich_04_utest.
parameters: p_bukrs like t001-bukrs obligatory.
start-of-selection.
lcl_app=>run_application( iv_bukrs = p_bukrs ). |
Global data |
*&---------------------------------------------------------------------* *& Include ZBC_GMICH_04_DATA *&---------------------------------------------------------------------*
tables: rlmob, t001.
data: begin of scr_100, bukrs type bukrs, work_days type i, end of scr_100. |
Classes – Definition & implementation |
*&---------------------------------------------------------------------* *& Include ZBC_GMICH_04_CLASS *&---------------------------------------------------------------------* data: ok_code like sy-ucomm.
class: lcl_local_gr definition deferred, lcl_local_ch definition deferred. *----------------------------------------------------------------------* * INTERFACE if_localazation *----------------------------------------------------------------------* * *----------------------------------------------------------------------* interface if_localazation. methods: init, calc_working_days returning value(ev_days) type i, get_alv_data exporting et_ltak type tt_ltak.
class-data: gt_ref type standard table of ref to if_localazation. events: obj_created exporting value(ev_bukrs) type bukrs. data: bukrs type bukrs. endinterface. "if_localazation
*----------------------------------------------------------------------* * CLASS LCL_APP DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* class lcl_app definition create private. public section. methods: constructor importing value(iv_bukrs) type bukrs.
class-methods: class_constructor, run_application importing iv_bukrs type bukrs, pbo, pai importing iv_code type sy-ucomm, exit_command importing iv_code type sy-ucomm.
private section. methods: set_status importing iv_status type string, reg_local_obj for event obj_created of if_localazation importing ev_bukrs sender, get_alv_data, set_alv_data, leave_screen.
class-methods: build_fieldcatalog, build_screen_object.
data: ref_local_gr type ref to lcl_local_gr, ref_local_ch type ref to lcl_local_ch.
class-data: bukrs type bukrs, screen_status type string, ok_code type sy-ucomm, fieldcat type lvc_t_fcat, gt_alv_data type standard table of ltak, gt_ref type standard table of ref to if_localazation.
class-data: ref_app type ref to lcl_app, ref_container type ref to cl_gui_custom_container, ref_alv type ref to cl_gui_alv_grid.
endclass. "LCL_APP DEFINITION
*----------------------------------------------------------------------* * CLASS lcl_local_gr DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* class lcl_local_gr definition. public section. interfaces: if_localazation. methods: constructor.
endclass. "lcl_local_gr DEFINITION
*----------------------------------------------------------------------* * CLASS lcl_local_ch DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* class lcl_local_ch definition. public section. interfaces: if_localazation. methods: constructor. endclass. "lcl_local_ch DEFINITION
*----------------------------------------------------------------------* * CLASS LCL_APP IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* class lcl_app implementation. method class_constructor. call method build_fieldcatalog. call method build_screen_object. endmethod.
method build_screen_object. create object ref_container exporting * PARENT = container_name = 'CUSTOM' * STYLE = * LIFETIME = lifetime_default * REPID = * DYNNR = * NO_AUTODEF_PROGID_DYNNR = exceptions cntl_error = 1 cntl_system_error = 2 create_error = 3 lifetime_error = 4 lifetime_dynpro_dynpro_link = 5 others = 6 . if sy-subrc <> 0. * MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO * WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4. endif.
create object ref_alv exporting * I_SHELLSTYLE = 0 * I_LIFETIME = i_parent = ref_container * I_APPL_EVENTS = space * I_PARENTDBG = * I_APPLOGPARENT = * I_GRAPHICSPARENT = * I_NAME = * I_FCAT_COMPLETE = SPACE exceptions error_cntl_create = 1 error_cntl_init = 2 error_cntl_link = 3 error_dp_create = 4 others = 5 . if sy-subrc <> 0. * MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO * WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4. endif.
endmethod.
method get_alv_data.
clear: gt_alv_data[].
data: o_ref type ref to if_localazation. read table gt_ref with key table_line->bukrs = ref_app->bukrs into o_ref. if sy-subrc = 0. o_ref->get_alv_data( importing et_ltak = gt_alv_data ). endif. endmethod.
method set_alv_data.
call method ref_alv->set_table_for_first_display exporting * I_BUFFER_ACTIVE = i_bypassing_buffer = 'X' * I_CONSISTENCY_CHECK = * I_STRUCTURE_NAME = * IS_VARIANT = * I_SAVE = * I_DEFAULT = 'X' * IS_LAYOUT = * IS_PRINT = * IT_SPECIAL_GROUPS = * IT_TOOLBAR_EXCLUDING = * IT_HYPERLINK = * IT_ALV_GRAPHICS = * IT_EXCEPT_QINFO = * IR_SALV_ADAPTER = changing it_outtab = gt_alv_data it_fieldcatalog = fieldcat * IT_SORT = * IT_FILTER = exceptions invalid_parameter_combination = 1 program_error = 2 too_many_lines = 3 others = 4 . if sy-subrc <> 0. * MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO * WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4. endif.
endmethod.
method reg_local_obj. data: wa_ref like line of gt_ref.
move sender to wa_ref. wa_ref->bukrs = ev_bukrs. append wa_ref to gt_ref. clear wa_ref. free wa_ref. endmethod.
method run_application. if ref_app is bound. exit. else. create object ref_app exporting iv_bukrs = iv_bukrs . endif. endmethod. "run_application
method pbo.
if ref_app is initial. exit. endif.
call method ref_app->set_status( 'DISPLAY' ). endmethod. "PBO
method exit_command.
ref_app->ok_code = iv_code.
case ref_app->ok_code. when 'BACK' or 'CANC'. ref_app->leave_screen( ). endcase. endmethod. "EXIT_COMMAND
method pai. data: o_ref type ref to if_localazation. ref_app->ok_code = iv_code.
case ref_app->ok_code. when 'CALC'. read table gt_ref with key table_line->bukrs = ref_app->bukrs into o_ref. if sy-subrc = 0. scr_100-work_days = o_ref->calc_working_days( ). endif.
when 'CHAN'. ref_app->set_status( 'CHANGE' ).
when others. endcase. endmethod.
method leave_screen. set screen 0. leave screen. endmethod. "LEAVE_SCREEN
method constructor. bukrs = iv_bukrs.
set handler me->reg_local_obj for all instances.
create object ref_local_gr. create object ref_local_ch.
call method get_alv_data. call method set_alv_data.
call screen 100. endmethod. "CONSTRUCTOR
method set_status. screen_status = iv_status.
set pf-status screen_status. endmethod.
method build_fieldcatalog. call function 'LVC_FIELDCATALOG_MERGE' exporting * I_BUFFER_ACTIVE = i_structure_name = 'LTAK' * I_CLIENT_NEVER_DISPLAY = 'X' * I_BYPASSING_BUFFER = * I_INTERNAL_TABNAME = changing ct_fieldcat = fieldcat exceptions inconsistent_interface = 1 program_error = 2 others = 3 . if sy-subrc <> 0. * MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO * WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4. endif.
endmethod. "SET_STATUS endclass. "LCL_APP IMPLEMENTATION
*----------------------------------------------------------------------* * CLASS lcl_local_gr IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* class lcl_local_gr implementation. method constructor. raise event if_localazation~obj_created exporting ev_bukrs = '1000'. endmethod.
method if_localazation~init. call method if_localazation~calc_working_days. endmethod. "if_localazation~calc_working_days
method if_localazation~calc_working_days. ev_days = 25. endmethod. "if_localazation~calc_working_days
method if_localazation~get_alv_data. select * from ltak into table et_ltak where lgnum = '010'. endmethod.
endclass. "lcl_local_gr IMPLEMENTATION
*----------------------------------------------------------------------* * CLASS lcl_local_ch IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* class lcl_local_ch implementation. method constructor. raise event if_localazation~obj_created exporting ev_bukrs = '2000'. endmethod.
method if_localazation~init. call method if_localazation~calc_working_days. endmethod. "if_localazation~if_init
method if_localazation~calc_working_days. ev_days = 19. endmethod. "if_localazation~calc_working_days
method if_localazation~get_alv_data. select * from ltak into table et_ltak where lgnum = 'TRM'. endmethod.
endclass. "lcl_local_ch IMPLEMENTATION |
Modules |
*&------------------------------------------------------------------- *& Include ZBC_GMICH_04_MODULES *&------------------------------------------------------------------- *&---------------------------------------------------------------------* *& Module PBO OUTPUT *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* module pbo output.
lcl_app=>pbo( ).
endmodule. " PBO OUTPUT
*&---------------------------------------------------------------------* *& Module PAI INPUT *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* module pai input.
lcl_app=>pai( ok_code ).
endmodule. " PAI INPUT
*&---------------------------------------------------------------------* *& Module EXIT_COMMAND INPUT *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* module exit_command input.
lcl_app=>exit_command( ok_code ).
endmodule. " EXIT_COMMAND INPUT |
Special Tips
- Try to hiding thinks (Private or protected). If there is no reason to expose a method or data of the method, then there is no reason these to be public. We should hide the implementation from the outside world.
- The Screen Flow logic should be controlled by static methods
- Screen objects should be created via static method
- We cannot call an instance method from a static method of the same class.
Initially we are calling a factory method to bring the object into life.
Factory Method |
lcl_app=>run_application( iv_bukrs = p_bukrs ).
|
The method takes as an input the company code. This is a static method that will instanciate the application object. |
Method: run_application – Factroy method of class LCL_APP |
method run_application. if ref_app is bound. exit. else. create object ref_app exporting iv_bukrs = iv_bukrs . endif. endmethod. |
This method can be called, because it is static. So there is no need for an excising object of that class. Inside the method we are checking if there is an active object of the class, otherwise we are instantiate the “ref_app”. |
static constructor |
method class_constructor. call method build_fieldcatalog. call method build_screen_object. endmethod. |
We are building the screen object and the fieldcatalog |
instance constructor |
method constructor. bukrs = iv_bukrs.
set handler me->reg_local_obj for all instances.
create object ref_local_gr. create object ref_local_ch.
call method get_alv_data. call method set_alv_data.
call screen 100. endmethod. |
We are registering the event handler. We are cr instantiate the local business classes for all the individual countries and we are calling the first screen. |
Method: get_alv_data |
method get_alv_data.
clear: gt_alv_data[].
data: o_ref type ref to if_localazation. read table gt_ref with key table_line->bukrs = ref_app->bukrs into o_ref. if sy-subrc = 0. o_ref->get_alv_data( importing et_ltak = gt_alv_data ). endif. endmethod. |
We are use the “Polymorphism” technique to see what is the relevant business class for the specific company code. |