Practical Use of OOPs ABAP – Model View Controller (MVC). Part I

The subject (ABAP Objects) I would like to share with you here today might have already been discussed numerous times in different forums and blogs, but still, I decided to write this blog because I want to present my case with a real time SAP Business example. Trust me, I will not waste your time with Vehicle Class examples (remember cars, trucks etc) or Animal Class examples. My focus today would be a real project program with a selection screen, data fetching, and display.

Being a traditional ABAP developer I was actively participating in the tug of war between the ABAP traditional approach and ABAP object oriented approach. The question is therefore NOT about using ABAP Objects or not. It is about knowing When to use it and How to use it. Along with these questions, another question strikes in mind ‘How to utilize MVC Design Pattern in ABAP? Well, I am still working on this question to ascertain the best solution, especially when screen design comes into the picture.

Let’s describe the Complex MVC (Model View Controller) in simple words. ‘Isolate the business logic from the user interface to provide the flexibility to change business logic independently‘. Wow!! We have finished complex MVC in just one sentence. Now let’s see how we can decouple the logic and what are the upside of this approach.

The advantage of using this approach is to reuse the existing logic in multiple similar requirements. Let’s say we have a sales order report to prepare. We can design our Model (business logic classes) in such a way that the same Class (model) can be used for creating the different views like report, smart forms. This approach gives us the flexibility to reuse that logic as it is part of the Model class.

Here is a simple report to present a real time practical scenario.

Scenario 1:

Create a report to display the Sales Documents (sales order, quotation, contract) based on Plant.

Let’s design our Selection screen.

To follow MVC pattern, I have created three separate local classes.

  • Selection screen CL_SEL
  • Fetch Data CL_FETCH(Act as MODEL)
  • Display data CL_ALV (Act as VIEW).

1. Class for Selection Screen – CL_SEL:

We have one method here GET_SCREEN. This method will get the data from the selection screen.

This class CL_SEL will just get the data from selection parameters to make it available for our next class CL_FETCH. (All the validations we can perform in class CL_SEL, let’s say Plant validations, which we will cover it in next part)

2. Class for Database Fetch – CL_FETCH:

This class has two methods CONSTRUCTOR and FETCH_DATA.

CONSTRUCTOR: contain the ref of the selection class CL_SEL.

FETCH_DATA: will select the data from DB table.

Now if you notice here in CL_FETCH class we have created object of CL_SEL class, as the objective is to decouple as much as possible so we are just passing the reference in Constructor.

Q: Why in the constructor?

Ans: Simply because constructor method will get called automatically when the object is created.

Now if you see our FETCH_DATA method we are fetching the records based on our selection screen parameters which are in another class (Magic of using Object).

3. Class for Display – CL_ALV:

This class is acting as the View and we have two methods here CONSTRUCTOR and DISPLAY_ALV.

CONSTRUCTOR: contain the ref of class CL_FETCH.

DISPLAY_ALV: will display the data using SALV table FACTORY method.

Now we have created the Object and we are good to go for calling our Methods (so our dish is almost ready, we are simply garnishing our dish).

Did you Notice, we are using all the events of ABAP report?

This is a very simple report to demonstrate the concept of MVC so I am using local classes but it is always recommended to use global classes (i.e. class created in t-code SE24). One more reason behind local classes; we lazy ABAPers are very good in copy and paste options, so this is an extra tip for you.

Why global classes?

Ans: Being lazy ABAPers, we often don’t listen to our Experts. I did the same (ignored global class and used my local class in the program). After some time, I got another requirement. Using the same input parameters, develop another report which will send our forms in PDF format to our special Customers. Then I realized the power of MVC and global classes …. as yet gaining from my oversights.

Here is the fresh hot DISH from the oven i.e. our First Real OOPs Program.

DATA : lv_vbeln TYPE vbap-vbeln  .
* Create selection screen
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-000.
  PARAMETERS : p_werks TYPE vbap-werks OBLIGATORY.
  SELECT-OPTIONS : s_vbeln1 FOR lv_vbeln .
SELECTION-SCREEN END OF BLOCK b1 .
 
* Create selection screen class
CLASS cl_sel DEFINITION FINAL .
 
  PUBLIC SECTION .
    TYPES : t_vbeln  TYPE RANGE OF vbeln .
    DATA : s_vbeln TYPE t_vbeln .
    DATA : s_werks TYPE werks_ext  .
    METHODS : get_screen IMPORTING lp_werks TYPE werks_ext
                                   ls_vbeln TYPE t_vbeln .
ENDCLASS .
*&---------------------------------------------------------------------*
*&       CLASS (IMPLEMENTATION)  SEL
*&---------------------------------------------------------------------*
*        Get Selection Screen Information
*----------------------------------------------------------------------*
CLASS cl_sel IMPLEMENTATION.
 
  METHOD get_screen .
    me->s_werks = lp_werks.
    me->s_vbeln = ls_vbeln[] .
  ENDMETHOD .
 
ENDCLASS.               "SEL
 
* Create DATA MODEL to  fetch  recordes
CLASS cl_fetch DEFINITION  .
 
  PUBLIC SECTION .
    DATA : sel_obj TYPE REF TO cl_sel .
    DATA : it_vbap TYPE STANDARD TABLE OF vbap .
 
    METHODS constructor IMPORTING ref_sel TYPE REF TO cl_sel .
    METHODS : fetch_data .
 
ENDCLASS .
*&---------------------------------------------------------------------*
*&       CLASS (IMPLEMENTATION)  FETCH
*&---------------------------------------------------------------------*
*        Fetch Sales Info
*----------------------------------------------------------------------*
CLASS cl_fetch IMPLEMENTATION.
 
  METHOD constructor.
    me->sel_obj = ref_sel .
  ENDMETHOD .
 
  METHOD fetch_data .
    SELECT * FROM vbap INTO TABLE me->it_vbap UP TO 10 ROWS WHERE vbeln
    IN me->sel_obj->s_vbeln AND werks EQ me->sel_obj->s_werks .
  ENDMETHOD .
 
ENDCLASS.               "FETCH
 
* Display data class
CLASS cl_alv DEFINITION .
 
  PUBLIC SECTION .
    DATA : fetch_obj  TYPE REF TO cl_fetch .
    METHODS : constructor IMPORTING ref_fetch TYPE REF TO cl_fetch.
    METHODS : display_alv .
 
ENDCLASS .
*&---------------------------------------------------------------------*
*&       Class (Implementation)  CL_ALV
*&---------------------------------------------------------------------*
*        Display the Output
*----------------------------------------------------------------------*
CLASS cl_alv IMPLEMENTATION.
 
  METHOD constructor .
    me->fetch_obj = ref_fetch .
  ENDMETHOD .
 
  METHOD display_alv .
 
    DATA: lx_msg TYPE REF TO cx_salv_msg.
    DATA: o_alv TYPE REF TO cl_salv_table.
 
    TRY.
        cl_salv_table=>factory(
          IMPORTING
            r_salv_table = o_alv
          CHANGING
            t_table      = me->fetch_obj->it_vbap ).
      CATCH cx_salv_msg INTO lx_msg.
    ENDTRY.
 
    o_alv->display( ).
 
  ENDMETHOD.
 
ENDCLASS.               "CL_ALV
 
* Declare TYPE REF TO class objects
DATA: o_sel     TYPE REF TO cl_sel,
      o_fetch   TYPE REF TO cl_fetch,
      o_display TYPE REF TO cl_alv.
 
INITIALIZATION .
* Creating Objects of the Class
  CREATE OBJECT : o_sel,
                  o_fetch   EXPORTING ref_sel = o_sel, " ref_sel is in Constructor
                  o_display EXPORTING ref_fetch = o_fetch. " ref_fetch is in Constructor
 
START-OF-SELECTION .
* Import screen data to class screen
  o_sel->get_screen( EXPORTING lp_werks = p_werks ls_vbeln = s_vbeln1[] ) .
 
* Call fetch data to fetch the records
  o_fetch->fetch_data( ).
 
END-OF-SELECTION .
* Display data
  o_display->display_alv( ).