Skip to Content

Intro

Nearly two years ago I had a discussion on how one could introduce Test Driven Development and unit testing into old fashioned procedural code esp. into function groups. The outcome was that a bigger refactoring of the function group into OO-artifacts would very often make sense, but there was no clarity, no hints nor best practices on how to do that.

A major issue was the poor knowledge of ABAP-OO in that group. Once again I heard the sentence: ‘We cannot use ABAP-OO as our developers do not know it.’.

Boy, how many times in my 25 years of abaping have I heard such things 😐

Irritated I started a comparison which led to a longer presentation including some demos.

As I see some blogs and discussions here, I decided to put my absolute personal and totally biased ideas 😉  from then into a small blog.

  1. At first I will talk about the reasons for and against a migration and an overall picture.
  2. Then I will handle the conversion and the reasonable and necessary code adaption.
    Part 2 of 3
  3. Finally, the necessary adaptions in the callers to use the new code artifacts are described.
    Part 3 of 3

 

Attention:

It is absolutely clear that the process and recommendations described in this blog series will never cover 100 percent.

There is no silver bullet. 

 

Motivation of Migration

Arguments related to testing

  • Function modules and their function groups are testable, but not on the same support level as ABAP-Classes.
  • Not only the testability of the function group and modules themselves are obstructed, also the tests of the callers are impeded. A mocking using interfaces is way easier than using function modules.
    And by example the ABAP Test Double Framework needs global interfaces.
    This differentiates the creation of classes from the usage of test-seams.

Arguments related to architecture

  • “Programming against interfaces” is a lot easier with ABAP-OO.
  • Function modules names are globally unique. Seeing the length of 30 characters good naming is very restricted as namespaces use lots of the name.
  • Function module calling is a dynamic call, so syntax check is done not on design time but on runtime.
  • The clarity of parameter types is better in methods then in function modules. No undirected parameter types like tables any more.
  • Forms can be called externally , so no call restriction here, leading to high cross-linking.
  • The very helpful ADT-based refactoring tools (like the extraction of code to an instantly created method to reduce code duplicates) are usable only with class based code.
  • Modern and recommended SAP Frameworks like BOPF are using OO-Artifacts.
    Switching from classes to procedural code, often a lot of times within the code structure, increases the amount of code complexity thus increasing the error probability.

Finally, I want to recommend an article series published by Horst Keller and Andreas Blumenthal in 2006: “An insider’s guide to writing robust, understandable, maintainable, state-of-the-art ABAP programs”; at least this part: http://www.sapwiki.cl/wiki/images/9/90/Abap_top_part_2.pdf

Discouraging of refactoring

After telling why to migrate I need to mention the arguments against the migration and of course any disruptive refactoring.

Arguments against disruptive refactoring

Enhancements and Modifications

Any refactoring has the problem that ABAP allows implicit enhancements and modifications and free usage of programming objects. By example there is no technical mean to forbid the call of any function module (apart from authorization checks, but that is checked only on run time).

In blog S4HANA Extensibility Concept this issue is also dicussed.

Unfortunately there is no cross-system general collection of enhanced programming objects.

Modifications are collected and within a system there are tools investigating the usages but if one has many customers/receivers this is not enough.

So there are two extremes:

  1. do not refactor anything disruptively or
  2. rely on the well known SPAU mechanism and a simplification/deprecation mechanism (and pray).

Deprecation Management

If you want to replace old programming objects with new programming objects starting at a new release/SP/FP you somehow put the maintenance at risk. You have to make the connection old to new transparent and you may never loose this transparency.

The same deprecation management is not only needed for maintenance but also for information management. The users calling the code to be refactored need to be activated. Once again the SPAU mechanism is relevant.

See also chapter ‘Post Generation Effort’.

Arguments against migration of Function Groups

  • Not all function groups can be migrated.
  • Some could be migrated after a split.
  • Some may be migrated but a code adaption would connect this change to a vast amount of other programs.
  • Some migration would become too costly.

So there is no one size fits all.

Absolutely impossible

Generated function groups cannot be migrated. This is especially true for function groups used in Table View Maintenance. They cannot be migrated as this framework needs function groups and generates its own code.

Partly impossible

Some content of a function group may impede a migration. The developer should think about a split of these function groups to separate the migratable parts from the prohibiting ones.

Another aproach would be to extract the content of function groups to classes and use the function modules as wrapper. This approach is thoroughly described in chapter ‘Post Generation Effort – Approach 1’.

  • Function groups may have dynpros defined. ABAP-OO lacks the ability to process dynpros. This split would also follow the Model-View-Controller concept.
  • Function modules having technical settings prohibiting a migration to ABAP-OO.
    • RFC
      This would separate the Import-code from other code easing the migration from RFC-technique to ODATA.
    • Update Task
      Update Task is related to Database affine code areas so this should be separated to ensure DB-layering.
  • Function modules used in frameworks prohibiting a migration to ABAP-OO.
    • Searchhelps
    • BDT and other frameworks

Reasons for not migrating migratable function groups

Some function groups may be technically migratable, but trigger a lot of effort. This may be ok if one wants to get a package free of procedural code or to get an often used callstack clean or other reasons like that. The following paragraphs are true for every disruptive refactoring, not only for the migration function group to ABAP-OO.

Massively connected function groups

If function modules are heavily used in a lot of function groups/classes/reports a migration with caller adaption would lead to a big list of changed programs.

In this case the priority of migration is to be questioned. I recommend to work on other function groups first.

Heavy usage of dynamic calls

If a function group heavily uses dynamic calls like “call function lv_function_name” or “perform (‘LV_FORMNAME’) in program (‘LV_PROGNAME’)” the investigation effort might become very high.

Better use the effort on other stakes.

OOP versus class-based code

One word about OOP. This blog is not about converting a function group into OOP.

With microservices and RESTful programming in my opinion pure OOP may not be the right direction. See https://blogs.sap.com/2017/12/07/be-prepared-for-the-new-abap-programming-model-in-sap-s4hana/
Quite in contrary; following the renaissance of functional programming style one should try to get rid of mutable static and instance attributes, thus the close coupling of data and methods.

Implementation / design patterns

Some of the following patterns refer to Design Patterns – Elements of Reusable Software“ of the Gang of Four [GOF1995].

Some patterns need to be discussed as I follow them henceforth.
I will talk about them only briefly, and why I use them. 

Static versus Instantiable Classes

Nearest to a function group would be a static class with static methods.

Some say static is the root of evil. Although I nearly absolutely second this statement I want to stress some statements on another level.

  • Static methods are not redefinable which is quite useful in mocking techniques.
  • Static variables 1: They are the oposite of side effect free programming as they are valid cross-instance. So even in instantiable classes they should be thoroughly checked to get rid of them.
  • Static variables 2: There is one exception: If you have a factory class managing instances. Then the managing table of instances has to be static.

I decided for myself not to use static classes at all.

Singleton

Beside a static class next nearest thing to a function group is a class with instance components using the singleton pattern.

But there are some disadvantages of singleton, as it is problematic for side-effect-free programming. Instance variables are becoming variables with a static behaviour.

Personally I do not recommend nor discourage it in the first step of migration.
But one should question the usage to ease side-effect-free programming.

See also https://en.wikipedia.org/wiki/Singleton_pattern

Program to an interface, not implementation.

In my point of view this is all about stability, robustness and reliability. I want to rely on a stable API, not on an implementation. Apart from a far better testability, this allows also better code evolution, multi-team-development and parallel development.

This is not only true for my program, but also for the callers of my program. They can start developing right after definition of the interface. They can implement their stubs and mocks to be used for their program.

See also https://en.wikipedia.org/wiki/Design_Patterns#Introduction,_Chapter_1

Interfaces

Best way to build a clear facade/contract to my callers is to have one or more interfaces. I strongly recommend to have separate interface for each group of callers. By example one could think of creating an interface for all the read-only callers and one for callers using write methods.

But for this task ( migration ) a 1:1 interface is sufficient.

Private instantiation

This is directly connected to the interface. If I want to rely only on an interface the caller should not be capable to instantiate the class but use a factory method to get a reference to an interface he needs.

Factory methods (versus factory classes)

In ‘private instantiation’ the use of a factory method is requested. But there are reasons to use a factory class instead.

In this document i restrict myself to a factory method to ease understanding.

Side-effect free programming

This is often claimed as one of the main advantages of functional programming (languages), but I interpret it also as prerequisite for FIRST class tests. If a test should be repeatable side-effects like database contents, configuration, stateful variables et al. are to be avoided.

Especially all about buffering of configuration et al. should be extracted into a separate (local) class.

Migration patterns at a glance

Components

The components of a function group could be matched to components of Interfaces and classes. Henceforth I describe shortly, which components would be migrated or could be compared to the other.

From To
Function Group Component OO Component Comment
Function Modules (Signature) Interface Instance Methods
Form Routines Private Instance Methods
Events (Load-of-program) (Class Constructor) I don’t recommend that, as most LOPs i see contain code to read configuration, which should be extracted into a separate configuration access class.
Global Variables Private Instance Attributes or
Private Static Attributes

Check if they are really needed, not for buffering configuration by example

Depends on usage of Singleton.

If singleton is used no static attributes are needed at all.

Reduce the amount of attributes as much as possible.

Statics Private Static Attributes Check if they are really needed. Often they are used for configuration buffering, too
If this is true=>config access class.
Constants / Types
  • Private types and constants of the class,
    if used only within the class
  • Types/constants in the created interface,
    if used in the former function module
    signature
  • Types/constants in separate interfaces, if they are widespread used in multiple classes
    These interfaces are the successor of the often used constant includes.
Macros Macros Try to create a separate class
Local test classes in *T99-include Local test class
Local classes Local classes

Parameters

The parameters should get the same names in the first refactoring step ignoring any naming conventions.

From To  
Function module Form routine Method Comment
Using Importing This may lead into problems, as though the distinction between using and changing is recommended for decades(sic!) it is not forced. It may occur that using parameter values are changed which is not allowed for importing parameters in methods.
Tables Tables Changing

If the tables parameter is defined via a structure a new table type (standard table type with default key) needs to be created either in interface or in the class and used instead of the old parameter type.

Tables-parameter have no direction. Therefore i put them to changing at first sight, but one should investigate, if importing, exporting or returning wouldn’t be suitable.

Exceptions They should be converted to a class-based exception having all the exceptions of the function group as constants. This exception class is then used in the exception raising of the method
Changing Changing
Changing Exporting Arguable, may be better converted to changing or returning
Importing Importing
Raising Raising Raising

 

 

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. Joachim Rees

    Thanks for sharing, Joachim

    I hope this indeed helps your SAP-Collegues to convert some Function Groups to ABAP-OO Artifacts!

    best
    Joachim

    PS: The buildup of the tilte (” the manual way “) hints that there might be another way (an (semi-?)automatic one?), too?
    I take we can lok foreward to more blogs from you? 🙂

    (1) 
  2. Jelena Perfiljeva

    Nice blog, thanks for sharing!

    Even though there have been no plans to work on migration in my past and current job, I just stopped creating any new FMs (other than RFC / special output ones) some time ago.

    Don’t have ADT / don’t care about Unit Test, I simply like the shorter method calling syntax. Exception handling is more streamlined. Names make more sense (function group really had no meaning before), not to mention no more stuff like some genius declared TABLES LIPS in a top include and then referenced it all over the place in 5 other includes. Bleh.

    I’m also curious about the non-manual way. 🙂

    (0) 
  3. Joachim Gross Post author

    Hi Joachim, hi Jelena,

    well, we are not allowed to talk about future developments , so i will not answer the non-manual way;-)

    And by the way, at SAP the chapter ‘Arguments against disruptive refactoring’ is way more important that at customer’s side:-(.

    @ Jelena Pavlica : you say you like lthe streamlined and shorter syntax with the better naming. That is why i stressed the architectual arguments in my blog. Personally, I love unit tests, but by example I see test doubles not as test doubles, but as decoupling technique for separation of concern used also for test doubling.

    This blog is titled with ‘migration’ but one can obviously use all my processes and recommendings also for the simple case of developing new objects, where one has to decide which way to go.

    (1) 

Leave a Reply