Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

In this blog, I will not be discussing about Object Oriented Concepts and theory as there are many documents and information available regarding the same. I am gonna discuss about how to start thinking in object oriented rather than procedural approach, how to implement or use the object oriented concepts while writing your code by benefiting your code performance, making your code compact and more reusable etc.

THINK OO

It is easy for an ABAPer who has been coding in ABAP(procedural) to code in ABAP OO by defining a local class and using methods instead of performs but that is not object oriented! To make use of object oriented concepts, you need to think OO. Think of what you need to develop in your code as objects.

You need to first design the requirement in Object oriented then it will be easy when you start coding.

For e.g. if you need to develop a report which displays a header and item alv then consider a parent class alv which has all the common attributes and methods which are inherited by 2 sub classes header and item alv. you can define the methods that are common for both in the parent class and methods that are different in each of the sub classes.

CODING IN OO

After designing when you are coding in OO, make use of the following points:

  • Standard classes/ methods: Try to use standard classes and methods available as much as you can. There are methods available for almost all function modules and more.
  • Re-usability: keep re-usability in mind and create methods which can be re-used more than once in your program.
  • Encapsulation: Encapsulate code into methods wherever you can.
  • Class-constructor and Constructor: When you want to initialize(assign values to variables/attributes) or perform a particular task once initially when a class object/method is called then use class-constructor. If you want to do the same when an object is created of the class then use constructor(instance constructor).
  • Interfaces: Make use of interfaces when two or more classes need common methods but with different logic/implementation.
  • Events: events are used when you want to trigger a particular method when an event happens. For e.g. Events for hot spot click and top of page.
  • Static and Instance attributes/methods: Static methods or attributes are used when you don't want it to change the whole time for that particular time but Instance attributes value changes for each object.
  • Abstract classes: When there are certain methods which we do not want to use in the parent class but want to implement in the sub classes then we can make the class and those methods as abstract. If we do not want to use them in the subclass then make the subclass as abstract too but then if there is a sub class for this subclass which is not abstract these methods will need to be implemented there.
  • Alias: Alias is used when you want to shorten the name of the interface methods for a class.

SAMPLE PROGRAM CODE

To develop a Report for Purchase order using ABAP OOPS.

  • The selection screen contains Purchase order No and Date in Ranges.

Three radio button

  1. PO released list,
  2. PO rejected List
  3. PO Not released list
  • On selecting the corresponding radio button it should show the details for given selection screen values using container ALV.
  • The PO Description should be editable. On changing the description and saving, it should be updated to corresponding PO Number.
  • On clicking on PO number it should show Item details.
  • No Global data to be used in the report
  • The header and the item fields to be displayed in the ALV is the programmers choice

As per the feedback and comments I have received from all of you, I have corrected my code. I hope that I have been able to remove the flaws in my previous design. I am not posting screen shots this time as one complained that it disables them to copy paste my code if they want to try it.

In the main program, you can see that I have separated various parts of the code inside includes. It is not recommended to add an include unless there is at least 200 lines of code but top of page/ data definitions is usually written inside the include as the variables/objects can be reused by other programs in the package(I have very few definitions and hence I have removed the data definitions include).

REPORT  ZPO_OOPS.

TABLES: ekko.

INCLUDE ZPO_OOPS_SS.  "Selection Screen

INCLUDE ZPO_OOPS_CDI. "Class Definitions and Implementations

DATA: o_purchaseorder TYPE REF TO cl_purchaseorder,

       o_cont1         TYPE REF TO cl_gui_custom_container,

       o_cont2         TYPE REF TO cl_gui_custom_container,

       o_grid1         TYPE REF TO cl_gui_alv_grid,

       o_grid2         TYPE REF TO cl_gui_alv_grid.

INCLUDE ZPO_OOPS_PBO. "PBO Modules

INCLUDE ZPO_OOPS_PAI. "PAI Modules

START-OF-SELECTION.

CREATE OBJECT: o_purchaseorder.

CALL METHOD o_purchaseorder->select_po.

Class definition deferred statement is used when we create the objects of the class before the actual definition and implementation. This way the debugger will come to know that the definition is written later in the code. If we do not define it later on then it will show an error.( I do not need to use the deferred statement here as I have declared classes after the definition).

Selection screen

SELECTION-SCREEN: BEGIN OF BLOCK po WITH FRAME TITLE text-009.

SELECT-OPTIONS: s_ebeln FOR ekko-ebeln,

                 s_aedat FOR ekko-aedat.

SELECTION-SCREEN: END OF BLOCK po.

SELECTION-SCREEN: BEGIN OF BLOCK rb WITH FRAME TITLE text-010.

SELECTION-SCREEN: BEGIN OF LINE.

PARAMETERS: r_releas  RADIOBUTTON GROUP r1.

SELECTION-SCREEN COMMENT 2(15) text-000 FOR FIELD r_releas.

PARAMETERS: r_reject  RADIOBUTTON GROUP r1.

SELECTION-SCREEN COMMENT 20(15) text-001 FOR FIELD r_reject.

PARAMETERS: r_no_rel  RADIOBUTTON GROUP r1.

SELECTION-SCREEN COMMENT 40(17) text-002 FOR FIELD r_no_rel.

SELECTION-SCREEN: END OF LINE.

SELECTION-SCREEN: END OF BLOCK rb.

I have used a controller interface just to separate the methods used for alv display.

INTERFACE i_controller.

   METHODS: alv_display EXPORTING cont TYPE REF TO cl_gui_custom_container

                                  grid TYPE REF TO cl_gui_alv_grid

                                  name TYPE scrfname,

            fc_header,

            on_click FOR EVENT hotspot_click

            OF cl_gui_alv_grid

            IMPORTING e_row_id.

ENDINTERFACE.                    "Interface for output


I have used one Purchase order class where the attributes and methods that are accessed by the program directly are declared in the Public section and the attributes that are used inside methods of the class are declared under Private section(Encapsulation). Class-constructor is used when you want to initiate the attributes when the class is accessed the first time. In this case I am assigning the layout structure initially.

*----------------------------------------------------------------------*

*       CLASS cl_purchaseorder DEFINITION

*----------------------------------------------------------------------*

*

*----------------------------------------------------------------------*

CLASS cl_purchaseorder DEFINITION.

   PUBLIC SECTION.

     CONSTANTS:  lc_cont1     TYPE scrfname VALUE 'HEAD_CON',

                 lc_cont2     TYPE scrfname VALUE 'ITEM_CON'.

     DATA:       lv_cont      TYPE scrfname.

     DATA:       lt_ekko TYPE STANDARD TABLE OF zpo_st_ekko,

                 lt_ekpo TYPE STANDARD TABLE OF zpo_st_ekpo.

     DATA: lt_ekko_tmp TYPE STANDARD TABLE OF zpo_st_ekko.

     CLASS-DATA: ok_code      TYPE syucomm.

     CLASS-DATA: lx_layout    TYPE lvc_s_layo.

     DATA:       lt_fcat      TYPE lvc_t_fcat.

     CLASS-METHODS: class_constructor.

     INTERFACES: i_controller.

     ALIASES: alv_display FOR i_controller~alv_display,

              fc_header   FOR i_controller~fc_header,

              on_click    FOR i_controller~on_click.

     METHODS:   select_po,

                save,

                back,

                exit.

   PRIVATE SECTION.

     CONSTANTS: lc_procstat1 TYPE meprocstate VALUE '01',

                lc_procstat4 TYPE meprocstate VALUE '04',

                lc_procstat5 TYPE meprocstate VALUE '05',

                lc_procstat6 TYPE meprocstate VALUE '06'.

     DATA: lv_ans   TYPE c.

     DATA: lv_procstat1 TYPE meprocstate,

           lv_procstat2 TYPE meprocstate.

     DATA: lx_fcat TYPE lvc_s_fcat.

     DATA: lx_ekko     TYPE zpo_st_ekko,

           lx_ekko_tmp TYPE zpo_st_ekko.

     DATA: lt_fieldcat  TYPE slis_t_fieldcat_alv,

           lx_fieldcat  TYPE slis_fieldcat_alv.

ENDCLASS.                    "cl_purchaseorder DEFINITION


I am created structures in the database to avoid building the field catalog, used the FM 'REUSE_ALV_FIELDCATALOG_MERGE' to get field catalog of a structure in order to modify it for hotspot and to make a field editable. Event hotspot_click is used for the method on_click. When we call set handler for this event, the method gets called each time the event is triggered. Constructor is called initially when the object of a class is created.

*----------------------------------------------------------------------*

*       CLASS cl_purchaseorder IMPLEMENTATION

*----------------------------------------------------------------------*

*

*----------------------------------------------------------------------*

CLASS cl_purchaseorder IMPLEMENTATION.


   METHOD class_constructor.

     lx_layout-zebra = 'X'.

     lx_layout-cwidth_opt = 'X'.

   ENDMETHOD.                    "class_constructor


   METHOD alv_display.

     CREATE OBJECT cont

       EXPORTING

         container_name = name.

     CREATE OBJECT grid

       EXPORTING

         i_parent = cont.

   ENDMETHOD.                    "alv_display

   METHOD select_po.

     IF r_releas EQ 'X'.

       lv_procstat1 = lc_procstat5.

       lv_procstat2 = lc_procstat5.

     ELSEIF r_reject EQ 'X'.

       lv_procstat1 = lc_procstat5.

       lv_procstat2 = lc_procstat6.

     ELSEIF r_no_rel EQ 'X'.

       lv_procstat1 = lc_procstat1.

       lv_procstat2 = lc_procstat4.

     ENDIF.

     SELECT ebeln

            bukrs

            description

            bstyp

            bsart

            aedat

            ernam

     FROM ekko

     INTO TABLE lt_ekko

     WHERE ebeln IN s_ebeln

     AND   aedat IN s_aedat

     AND   procstat BETWEEN lv_procstat1 AND lv_procstat2.

     IF lt_ekko IS NOT INITIAL.

       lt_ekko_tmp[] = lt_ekko[].

       CALL SCREEN 9000.

     ELSE.

       MESSAGE s000(8i) WITH text-011 DISPLAY LIKE 'E'.

     ENDIF.

   ENDMETHOD.                    "select_po

   METHOD back.

     CALL FUNCTION 'POPUP_TO_CONFIRM'

       EXPORTING

         text_question  = text-006

         text_button_1  = text-007                           "'Ja'(001)

         text_button_2  = text-008"'Nein'(002)

       IMPORTING

         answer         = lv_ans

       EXCEPTIONS

         text_not_found = 1

         OTHERS         = 2.

     IF sy-subrc EQ 0.

       CASE lv_ans.

         WHEN '1'.

           CALL METHOD me->save.

           LEAVE TO SCREEN 0.

         WHEN '2'.

           LEAVE TO SCREEN 0.

         WHEN 'X'.

       ENDCASE.

     ENDIF.

   ENDMETHOD.                    "back


   METHOD exit.

     CALL FUNCTION 'POPUP_TO_CONFIRM'

       EXPORTING

         text_question  = text-006

         text_button_1  = text-007                           "'Ja'(001)

         text_button_2  = text-008"'Nein'(002)

       IMPORTING

         answer         = lv_ans

       EXCEPTIONS

         text_not_found = 1

         OTHERS         = 2.

     IF sy-subrc EQ 0.

       CASE lv_ans.

         WHEN '1'.

           CALL METHOD me->save.

           LEAVE PROGRAM.

         WHEN '2'.

           LEAVE PROGRAM.

         WHEN 'X'.

       ENDCASE.

     ENDIF.

   ENDMETHOD.                  "constructor

   METHOD save.

     LOOP AT lt_ekko_tmp INTO lx_ekko_tmp.

       READ TABLE lt_ekko INTO lx_ekko WITH KEY ebeln = lx_ekko_tmp-ebeln.

       IF sy-subrc EQ 0.

         IF lx_ekko-description NE lx_ekko_tmp-description.

           UPDATE ekko

       SET description = lx_ekko-description

       WHERE ebeln = lx_ekko-ebeln.

           IF sy-subrc EQ 0.

             MESSAGE s000(8i) WITH text-005.

           ENDIF.

         ENDIF.

       ENDIF.

     ENDLOOP.

     lt_ekko_tmp[] = lt_ekko[].

   ENDMETHOD.                    "save

   METHOD fc_header.

     CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'

       EXPORTING

         i_structure_name       = 'ZPO_ST_EKKO'

       CHANGING

         ct_fieldcat            = lt_fieldcat

       EXCEPTIONS

         inconsistent_interface = 1

         program_error          = 2

         OTHERS                 = 3.

     LOOP AT lt_fieldcat INTO lx_fieldcat.

       MOVE-CORRESPONDING lx_fieldcat TO lx_fcat.

       lx_fcat-coltext = lx_fieldcat-seltext_l.

       IF lx_fcat-fieldname = 'EBELN'.

         lx_fcat-hotspot   = 'X'.

       ELSEIF lx_fcat-fieldname = 'DESCRIPTION'.

         lx_fcat-edit      = 'X'.

       ENDIF.

       APPEND lx_fcat TO lt_fcat.

     ENDLOOP.

   ENDMETHOD.                    "fc


   METHOD on_click.

     READ TABLE lt_ekko INTO lx_ekko INDEX e_row_id-index.

     IF sy-subrc EQ 0.

       SELECT ebelp

              werks

              lgort

              aedat

              txz01

              matnr

       FROM ekpo

       INTO TABLE lt_ekpo

       WHERE ebeln = lx_ekko-ebeln.

       IF sy-subrc EQ 0.

         CALL SCREEN 9001.

       ENDIF.

     ENDIF.

   ENDMETHOD.                    "on_click

ENDCLASS.                    "cl_purchaseorder IMPLEMENTATION

PBO

*&---------------------------------------------------------------------*

*&  Include           ZPO_OOPS_PBO

*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*

*&      Module  STATUS_9000  OUTPUT

*&---------------------------------------------------------------------*

*       Header screen

*----------------------------------------------------------------------*

MODULE status_9000 OUTPUT.

   SET PF-STATUS 'STATUS_9000'.

   SET TITLEBAR 'TITLE_9000'.

   IF o_grid1 IS INITIAL.

     o_purchaseorder->lv_cont = o_purchaseorder->lc_cont1.

     CALL METHOD o_purchaseorder->alv_display

       IMPORTING

         cont = o_cont1

         grid = o_grid1

         name = o_purchaseorder->lv_cont.

     CALL METHOD o_purchaseorder->fc_header.

     CALL METHOD o_grid1->set_table_for_first_display

       EXPORTING

         is_layout        = o_purchaseorder->lx_layout

*        i_structure_name = 'ZPO_ST_EKKO'

       CHANGING

         it_outtab        = o_purchaseorder->lt_ekko

         it_fieldcatalog  = o_purchaseorder->lt_fcat.

     SET HANDLER o_purchaseorder->on_click FOR o_grid1.

   ELSE.

     CALL METHOD o_grid1->refresh_table_display.

   ENDIF.

ENDMODULE.                 " STATUS_9000  OUTPUT

*&---------------------------------------------------------------------*

*&      Module  STATUS_9001  OUTPUT

*&---------------------------------------------------------------------*

*       Item Screen

*----------------------------------------------------------------------*

MODULE status_9001 OUTPUT.

   SET PF-STATUS 'STATUS_9000' EXCLUDING 'SAVE'.

   SET TITLEBAR 'TITLE_9001'.

   IF o_grid2 IS INITIAL.

     o_purchaseorder->lv_cont = o_purchaseorder->lc_cont2.

     CALL METHOD o_purchaseorder->alv_display

       IMPORTING

         cont = o_cont2

         grid = o_grid2

         name = o_purchaseorder->lv_cont.

     CALL METHOD o_grid2->set_table_for_first_display

       EXPORTING

         is_layout        = o_purchaseorder->lx_layout

         i_structure_name = 'ZPO_ST_EKPO'

       CHANGING

         it_outtab        = o_purchaseorder->lt_ekpo.

   ELSE.

     CALL METHOD o_grid2->refresh_table_display.

   ENDIF.

ENDMODULE.                 " STATUS_9001  OUTPUT

PAI

*&---------------------------------------------------------------------*

*&  Include           ZPO_OOPS_PAI

*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*

*&      Module  USER_COMMAND_9000  INPUT

*&---------------------------------------------------------------------*

*       text

*----------------------------------------------------------------------*

MODULE user_command_9000 INPUT.

   CASE cl_purchaseorder=>ok_code.

     WHEN 'BACK'.

       CALL METHOD o_grid1->check_changed_data.

       IF o_purchaseorder->lt_ekko_tmp NE o_purchaseorder->lt_ekko.

         CALL METHOD o_purchaseorder->back.

       ELSE.

         LEAVE TO SCREEN 0.

       ENDIF.

     WHEN 'EXIT' OR 'CANCEL'.

       CALL METHOD o_grid1->check_changed_data.

       IF o_purchaseorder->lt_ekko_tmp NE o_purchaseorder->lt_ekko.

         CALL METHOD o_purchaseorder->exit.

       ELSE.

         LEAVE PROGRAM.

       ENDIF.

     WHEN 'SAVE'.

       CALL METHOD o_grid1->check_changed_data.

       IF o_purchaseorder->lt_ekko_tmp NE o_purchaseorder->lt_ekko.

         CALL METHOD o_purchaseorder->save.

       ELSE.

         MESSAGE s000(8i) WITH text-012 DISPLAY LIKE 'E'.

       ENDIF.

   ENDCASE.

ENDMODULE.                 " USER_COMMAND_9000  INPUT

*&---------------------------------------------------------------------*

*&      Module  USER_COMMAND_9001  INPUT

*&---------------------------------------------------------------------*

*       text

*----------------------------------------------------------------------*

MODULE user_command_9001 INPUT.

   CASE cl_purchaseorder=>ok_code.

     WHEN 'BACK'.

       LEAVE TO SCREEN 0.

     WHEN 'EXIT' OR 'CANCEL'.

       CALL METHOD o_grid2->check_changed_data.

       IF o_purchaseorder->lt_ekko_tmp NE o_purchaseorder->lt_ekko.

         CALL METHOD o_purchaseorder->exit.

       ELSE.

         LEAVE PROGRAM.

       ENDIF.

   ENDCASE.

ENDMODULE.                 " USER_COMMAND_9001  INPUT

25 Comments