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: 
joachim_gross
Advisor
Advisor
1,526
This is part 2 of my series regarding migration function groups.

Starting Blog is Part 1 of 3

In this part I will handle the conversion and the reasonable and necessary code adaptions.


Exception class


Classic exceptions should be converted to class-based exceptions referring to a new exception class.

I use the 'message' addition of raise exception, so I do not need additional attributes for the message variables.

https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/index.htm?file=abapraise_exception_messag...

The exception classes in my example do no really differ any more, so one could think about using one exception class for a package.

Example














Signature of a function module Exception class
FUNCTION zjg_fm1_1.
*"------------------------------------------------------------
*"  IMPORTING
*"     REFERENCE(IV_GUID)
*"       TYPE  GUID_16
*"           DEFAULT '1234567812345678'
*"     REFERENCE(IF_FLAG) TYPE  CHAR_01
*"  TABLES
*"      RETURN STRUCTURE  BAPIRET2
*"  EXCEPTIONS
*"      NOT_FOUND
*"      SOME_ERROR
*"------------------------------------------------------------

class ZJG_CX_NEW definition public
inheriting from CX_STATIC_CHECK
create public.
public section.
interfaces IF_T100_DYN_MSG .
interfaces IF_T100_MESSAGE .

data FORMER_EXCEPTION type RS38L_PAR_ .
methods CONSTRUCTOR
importing
!TEXTID like IF_T100_MESSAGE=>T100KEY optional
!PREVIOUS like PREVIOUS optional
!FORMER_EXCEPTION type RS38L_PAR_ optional .



 

Interface


The interface should contain all method definitions for all function modules of the function group together with the related type definitions for tables parameter and constants used as default values.

So apart from DB-Entries every type and constants used in the methods should be defined here. Even if they are simply referring to global constant-/type interfaces. Hereby the caller has a clear contract what is to be expected.

I even recommend to define own types to define exactly the contract between caller and callee.

Naming


As the methods are now not globally unique any more one should follow the clean code rule to name the method describingly.

Example


A function group has one function module ZJG_FM1_1  as defined below.

In the function module the parameter if_flag should contain only 'X' or ' '.

Function module name ZJG_FM1_1 could be replaced by its true meaning: send message to other systems for a contract defined by the GUID.

=>send_message_for_contract.

 












Function Group Interface
in top include
constants: con_true type char_01 value 'X',
con_false type char_01 value ' '.



FUNCTION zjg_fm1_1.
*"----------------------------------------------
*"*"Lokale Schnittstelle:
*" IMPORTING
*" REFERENCE(IV_GUID) TYPE GUID_16
* "" DEFAULT '1234567812345678'
*" REFERENCE(IF_FLAG) TYPE CHAR_01
*" DEFAULT CON_TRUE
*" TABLES
*" RETURN STRUCTURE BAPIRET2
*" EXCEPTIONS
*" NOT_FOUND
*" SOME_ERROR
*"---------------------------------------------

 

 

interface ZJG_IF_NEW.

public .
types: TT_BAPIRET2 type standard table of BAPIRET2
with default key .
types: ty_flag type char_01.
constants:
con_true type ty_flag value 'X',
con_false type ty_flag value ' '.

methods send_message_for_contract
importing
!IF_FLAG type ty_flag default con_true
!IV_GUID type GUID_16 default '1234567812345678'
changing
!RETURN type TT_BAPIRET2
raising
ZJG_CX_NEW.
endinterface.



 

Class


Instantiation


Class with private instantiation (singleton if wished)

A factory method returning a reference to the interface should be created.

Example with singleton


class zjg_cl_new definition public create private final.
public section.
interfaces zjg_if_new.
class-methods: s_get_instance
returning
value(rr_instance) type ref to zjg_if_new.
private section.
data sr_instance type ref to zjg_cl_new.
endclass.

class zjg_cl_new implementation.
method s_get_instance.
if sr_instance is not bound.
sr_instance = new zjg_cl_new( ).
endif.
rr_instance = sr_instance.
endmethod.
method zjg_if_new~send_message_for_contract.
...do something
endmethod.
endclass.

 

Example without singleton


Class zjg_cl_new definition public create private final.
public section.
interfaces zjg_if_new.
class-methods: s_get_instance returning value(rr_instance) type ref to zjg_if_new.
endclass.

class zjg_cl_new implementation.
method s_get_instance.
rr_instance = sr_instance.
endmethod.

method zjg_if_new~send_message_for_contract.
...do something
endmethod.
endclass.

 

Definition


Former Form routines.


They are to be defined as described in the overview.

Exception class used in private methods


If an exception class is generated all private methods should get an exception based on this class. This is needed because in function methods and forms there is no rule when to raise classic exception. Those could be raised everywhere in the call stack. If one wants to replace the raisings all methods in the call stack need this exception class in their method signature.

Former global variables


In first step all variables formerly defined globally should be converted into

  • private instance attributes if singleton is used or

  • public static attributes if singleton is not used.


But they should be checked if they are really needed. Best would be to have as few as possible.

Even if it is decided to follow the OOP and not the functional programming style one should reduce the amount of attributes by grouping them into structures.

The typing of the attributes may not abide by the rules of ABAP-OO (e.g. table with header line) so here we might have a bit of adaption effort.

Implementation


The methods defined in the interface are to be implemented.

First all implementations should be adapted to abide by the rules of ABAP-OO. This could be easy or a bit more difficult.

An easy example would be to exchange a table defined with header line into one defined as standard table.

By example statics are not allowed in instance methods so one has to decide how to get rid of them. If they are buffers for configuration, by example, on should get rid of them by having a separate configuration access class.

ABAPUnit Testclass


A local unit test class should be created.

Improving the possibility to create automated tests is one of the key reasons for this whole activity.

Facade for Dependent-On-Components(DOC)


For separation of concern, DRY and unit test mocking all calls to external content (like function modules of other function groups) should be called via a facade.

This requirement is eased by creating a local DOC-class. It consists of

  • interface definition ‘lif_doc’ in class relevant local defs

    • for each external function module called a method with the name of the function module should be created with transformed parameters.



  • class ‘lcl_doc’ definition and implementation in local definition

    • each interface method should be implemented with the call of this function module without any parameters.



  • private attribute of type ‘lif_doc’

    • a private attribute of the interface reference should be created.



  • instantiation of this attribute in constructor method of generated class.


This approach is fully valid only for the first step of refactoring.

If the called function modules belong to a neighbor function group one should consider refactoring them, too. Then we are the callers of migrated function modules and should adapt our call to the new class/Interface.

Example


In function group ZJG_FUNCTION_GROUP three function modules of other function groups are called.

FUNCTION zjg_fm1_set_buffer.
FUNCTION zjg_fm1_1.
FUNCTION zjg_fm2.




















Function Module Signature
zjg_fm1_set_buffer FUNCTION zjg_fm1_set_buffer.
zjg_fm1_1. FUNCTION zjg_fm1_1.
*"  IMPORTING
*"     REFERENCE(IV_GUID) TYPE  GUID_16
DEFAULT '1234567812345678'

*"     REFERENCE(IV_FLAG) TYPE  CHAR_01
*"  TABLES
*"      RETURN STRUCTURE  BAPIRET2
*"  EXCEPTIONS
*"      NOT_FOUND
*"      SOME_ERROR
zjg_fm2 FUNCTION zjg_fm2.
*"  IMPORTING
*"     REFERENCE(IV_GUID) TYPE  GUID_16
*"  EXPORTING
*"     REFERENCE(RV_SUCCESS_FLAG) TYPE  CHAR_01


 












































Local interface
Method definition
Local class
Method implementation
Adaptions to the newly created class
LIF_DOC LCL_DOC ZJG_CL_NEW
  CLASS lcl_doc DEFINITION.
PUBLIC SECTION.
INTERFACES lif_doc.
ENDCLASS.


CLASS ZJG_CL_NEW definition.

private section.

methods constructor.

data mr_doc type ref to lif_doc.
TYPES tt_bapiret2
   TYPE STANDARD TABLE OF
bapiret2

WITH DEFAULT KEY.
 


class zjg_cl_new implementation.

method constructor.

mr_doc = new lcl_doc( ).

endmethod.
  METHODS zjg_fm1_set_buffer. METHOD lif_doc~zjg_fm1_set_buffer .
CALL FUNCTION
'ZJG_FM1_SET_BUFFER'.
ENDMETHOD.
METHODS zjg_fm1_1
IMPORTING
iv_guid TYPE guid_16
iv_flag TYPE char_01
  CHANGING
    return  TYPE tt_bapiret2
EXCEPTIONS
not_found
some_error
.


METHOD lif_doc~zjg_fm1_1 .

CALL FUNCTION 'ZJG_FM1_1'
EXPORTING
iv_guid    = iv_guid
iv_flag    = if_flag
TABLES
return     = return
EXCEPTIONS
not_found  = 1
some_error = 2.

Case sy-subrc.
When 0.
    When 1.
      Raise not_found.
    When 2.
      Raise some_error.
    When others.
  Endcase.
ENDMETHOD.
METHOD lif_doc~zjg_fm2 .
CALL FUNCTION 'ZJG_FM2'
EXPORTING
iv_guid         = iv_guid
IMPORTING
rv_success_flag = rv_success.
ENDMETHOD.


Legend

Type TT_BAPIRET2 created in interface to be used in method signature. See green markings.

 

Code Adaptions


In the next chapters i do not talk about the code adaptions triggered by ABAP-OO itself. As ABAP-OO is a lot stricter than procedural ABAP there is always some effort of adaption this covers change of local tables getting rid of header lines, field-symbols defined globally, other typing rules etc.
The effort occurring here depends a lot on the way programs were written. Some language adaptions are easy others not.

Instead the following chapters describe the code adaptions triggered by the refactoring itself, like the change to exception classes.

Call replacement


The newly created methods for form routines and function modules need to be called. Also the raisings of classical exceptions in the former function modules and forms need to be replaced with the raise of the new exception class.

Replacement of proprietary calls


The calls of the former form routines and function modules being proprietary of the function group should be replaced by the calls of the newly created methods.

Example

The code of method zjg_if_new_class_wrapp6~zjg_fm6_1 should be adapted to call the new methods zjg_if_new_class_wrapp6~zjg_fm6_2 and f01.

The change of call of former form routines is also a change of paradigm. Form routines have a parameter call/definition by position, function modules and methods by keyword. So on change of call it has to be adapted to parameter by name behavior. See also Hardgrave. (1976). Positional versus keyword parameter communication. ACM SIGPLAN Notices, 52-58.

The change of tables parameters to changing parameters makes this worse.
Form routines are called with parameter sequence tables, using, changing, raising.
Methods are called with a parameter sequence exporting, importing, changing, returning, raising.

See blue marking in code example.

Restrictions


Dynamic calls often prohibit an easy replacement. See yellow marking in code example.

Worst situation is if there is a call function with dynamic function module name and parameter table.

Here one have to investigate thoroughly in which cases which function module with which parameters is called.

Example: i once saw a function module having two parameters which were not allowed to be used simultaniously. Get and change-function module had the same signature.

One developer decided to forgo this situation by

  1. using fully dynamic call and

  2. filling the parameter table case-driven.


Replacement of external calls


The calls of the external function modules should be replaced by the calls of the DOC-methods.
See green marking in code example.
Example





















Old Function Module New Method
ZJG_FM6_1 zjg_if_new_class_wrapp6~zjg_fm6_1


FUNCTION ZJG_FM6_1.

DATA lt_rettab TYPE bapirettab.
DATA lv_c1 TYPE char64 VALUE 'ABCD'.

CALL FUNCTION 'ZJG_FM1_1'    " FG external FM
  EXPORTING
*       IV_GUID = '1234567812345678'
    if_flag = abap_false
  TABLES
    return  = lt_rettab.

CALL FUNCTION 'ZJG_FM6_2'  " FG propriety Fm
  EXPORTING
    if_flag = abap_false
  TABLES
    return  = lt_rettab
  CHANGING
    cv_1    = lv_c1
.

****************************
DATA lv_out TYPE char_01.
DATA lv_progname TYPE progname.
DATA lv_formname TYPE edcompont.

lv_progname =
'SAPLZJG_FUNCTION_MOD_AS_WRAPP3'.
lv_formname = 'GET_ANYTHING'.
  PERFORM (lv_formname)
IN PROGRAM (lv_progname)

                      USING '1' CHANGING lv_out.

 

****************************
DATA lt_01_itab TYPE bapirettab.
DATA lt_02_struc TYPE STANDARD TABLE OF
bapiret2
WITH DEFAULT KEY.
DATA lv_u1 TYPE string.
DATA lv_c01 TYPE string.

PERFORM f01 TABLES        lt_01_itab lt_02_struc
                         USING          lv_u1
                         CHANGING lv_c01.

 

ENDFUNCTION.


METHOD zjg_if_new_class_wrapp6~zjg_fm6_1.

DATA lt_rettab TYPE bapirettab.
DATA lv_c1 TYPE char64 VALUE 'ABCD'.

mo_doc->zjg_fm1_1(
  EXPORTING
    if_flag = abap_false
  CHANGING
    return = lt_rettab
  ).

zjg_if_new_class_wrapp6~zjg_fm6_2(
  EXPORTING
    if_flag = abap_false
  CHANGING
    return = lt_rettab
    cv_1 = lv_c1
  ).

 

***********************************
DATA lv_out TYPE char_01.
DATA lv_progname TYPE progname.
DATA lv_formname TYPE edcompont.

lv_progname =
'SAPLZJG_FUNCTION_MOD_AS_WRAPP3'.
lv_formname = 'GET_ANYTHING'. PERFORM (lv_formname)
IN PROGRAM (lv_progname) 

                    USING '1' CHANGING lv_out.

 

*********************************
DATA lt_01_itab TYPE bapirettab.
DATA lt_02_struc TYPE STANDARD TABLE OF
bapiret2
WITH DEFAULT KEY.
DATA lv_u1 TYPE string.
DATA lv_c01 TYPE string.

f01( EXPORTING u_1 = lv_u1
       CHANGING   t_01_itab = lt_01_itab
                             t_02_struc = lt_02_struc
                             c_1 = lv_c01
      ).

ENDMETHOD.


Legend

dangerous call, to be investigated thoroughly

Replaced proprietary call

Replaced external call


Replacement of exception raisings




















  Raise of classic exception Raise of new exception class
Pattern Raise old_exception Raise exception type new_exception_class
  exporting
former_exception = old_exception.
Example RAISE not_found. RAISE EXCEPTION TYPE zcx_abc
  EXPORTING
    former exception = 'NOT_FOUND'.


 
Replacements of message raisings

Static message raisings should be replaced with the raise of the newly created exception class. Dynamic message raisings need to be handled manually.




























  Message of classic exception Raise of new exception class Comment
Pattern Message t100 with v1 v2 v3 v4 raising old_exception.   Raise exception type
new_exception_class
  message
id t100-msgid
type t100-msgty
number t100-msgno
with v1 v2 v3 v4
exporting
former_exception = old_exception.
The T100 key can be implemented in 4 different types,

  1. Message e000(D0)

  2. Message e(000) using the message class of the function group

  3. Message type ‘E’
    ID ‘D0’
    NUMBER ‘000’

  4. Message type ‘E’
    NUMBER ‘000’


Example Message e001(D0) with ‘ANTON’ RAISING ERROR. RAISE EXCEPTION TYPE zcx_abc
Message
ID ‘D0’
Type ‘E’
NUMBER ‘001’
With ‘ANTON’
EXPORTING
former_exception = ‘ERROR’.
Short form of T100-Key used in message
Example

Message TYPE ‘E’

NUMBER 001

with ‘ANTON’ RAISING NOT_FOUND.
RAISE EXCEPTION TYPE zcx_abc
Message
    ID ‘D01’
    Type ‘E’
    NUMBER ‘001’
With ‘ANTON’
EXPORTING
former_exception = ‘NOT_FOUND’.
The function group has a message ID D01 assigned.


Pitfalls


Function modules


Parameters names


In function modules it is allowed to have an importing parameter and an exporting parameter with the very same name. This is not allowed in ABAP-OO.

Parameter typing


Parameters in function modules refer to generic types or DDIC-Types. These can be defined with ‘Like’. Parameters in method definition referring to generic or DDIC-types must be typed with ‘TYPE’. Therefore all ‘Like’s in definition of parameter of method are to be changed to ‘TYPE’s.
Labels in this area