Application Development and Automation 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: 
BaerbelWinkler
SAP Champion
SAP Champion
3,812
Those of you who have been following my blog posts and reading my questions are well aware that I'm having a hard time wrapping my head around anything ABAP-OO. Over the last months, I've however taken the first tentative steps and at least written a couple of report programs utilising local classes and methods instead of doing everything with forms. Of course, these were nothing fancy, just some data retrieval, processing and generating ALV-lists or output files, learning about and using some neat RTTS classes while doing that to quickly get things like field descriptors for column headers.

Fair warning of what's to come


This blog-post is perhaps a bit unusual in that I first describe what I already have and will then ask for help to - potentially - switch my working form-based code to making use of a local class with relevant methods instead.

So, I recently created a new function group and module, well knowing that I perhaps shouldn't have - but old habits die hard and my first goal was to get "something" working out there instead of having to simultaneously deal with the logic itself plus not really knowing what I needed to do where with ABAP OO. The gist of what the function module does is to apply several checks on an entered but not yet saved transport title when a transport is newly created. It will eventually be called from Badi CL_IM_CTS_REQUEST_CHECK in method if_ex_cts_request_check~check_before_creation but at the moment I still have it just as a standalone function module for ease of testing via SE37 and at the same time not annoying my colleagues while testing stuff. That'll come soon enough!

What I already have developed


Here is the gist of what the function module does and uses to do its stuff:

  • A Z-table to define the developer type based on the username (we can tell via the username if it's a regular user or one used for special purposes like e.g. data dictionary activities)

  • A Z-table to define which component is expected to be included in the transport title (change-identifier, country-identifier, developername)

  • A Z-table to define allowed identifiers, either as plain text (e.g. "Basis", "ProjextXYZ") or a REGEX pattern (e.g. "1\d-\d\d\d\d-C for a change-ID looking like "18-0815-C")

  • Incoming parameters are just the username and entered transport title (AS4TEXT) plus the transport-type with "K" for "Workbench" being the default. I'll most likely start with just calling the FM for workbench-transports and ignore customizing ones which usually aren't tackled by developers.  Not to mention that trying to enforce dev-guidelines for customizing activities is a completely different kettle of fish!

  • To begin with, I get the needed information from the Z-tables so that I know what needs to be looked for in the transport title as combinations of IDENTIFIER, COUNTRY and DEVNAME are possible dependent on determined usertype

  • In a loop, the expected identifiers are tackled and the title gets queried for each of them in turn in its own form-routine (yeah, I know, the "f-word"!)

  • If an identifier is missing, an entry is added to the table eventually used when function module POP_UP_TO_GET_VALUES gets called to ask for any missing identifiers in, well, a pop-up!

  • When the user gets the pop-up it's only asking for the missing values and also shows the incoming transport title

  • The pop-up cannot be exited without providing valid input, which means that the earlier checks are done again, just based on different fields

  • Error messages with long explanations are provided to make it clear what is expected for each field

  • Once all checks are satisfied, the title is accepted and the function module has done its task


Where to go from here


Apart from some fine-tuning, the function module is basically good to go into the Badi and that would obviously be the easy way out for me. It would however most likely also not be the best way out and I'm well aware that some options available with ABAP OO - like unit testing - would have come in handy for at least some of the on the spot testing while putting the code together. And no, I wasn't even contemplating TDD for this as I actually wanted to get something done fairly quickly and not having to deal with one more complication on top of everything else.

Sooo, long story not so short, what I'd like to do next is to somehow introduce a local class in the function module and to then - step-by-step - switch one form routine after the other to a method. I'm even contemplating leaving the tested and working form-logic in the code for now and to temporarily introduce a flag via which I can make the function module either go the existing form- or the new method-route. That way, I'll for one be able to do side-by-side testing and for another really see and thereby learn where things stay the same and what needs to or should be changed. This may be way over the top for many of you reading this but it's the way I learn best.

What I already saw in the function group's main program is a commented out line for INCLUDE <fg-name>P... with a tentalizing comment next to it indicating that it's for "Local class implement". So this to me looks as if what I'd like to do is possible, even more so as the help for function modules also mentions to use calls to methods instead of forms in order to follow the ABAP OO guidelines. My problem is, that I haven't found an example of what I actually need to do where and in which sequence.

Anybody interested to help me with this? Mind you, I'm not asking for done and dusted code, just some guidance of what I need to add where, how to call things and how to properly copy my code from form-routines to identically working local methods. I don't necessarily plan to go all-in, meaning that my main goal is to move things somewhat in the right direction but it doesn't have to be picture-perfect by dotting all the i's and crossing all the t's for "pure ABAP OO" unless it's easy to do and understand for me.

Once this is done and dusted, I'm fairly certain that a follow-up blog post will see the light of day, potentially with some side by side code. That's at least what I'd like to get from this exercise for future reference.

Any takers?
33 Comments
Patrick_vN
Active Contributor
Best way to learn ABAP-OO is to try doing it! And then do some more theory-crafting and improve. (Or I hope it's the best way 'cause I'm doing it like that).

Anyway, I can explain (or give some pointers) on how I'd do it. Up to you to decide whether it is enough :D. Currently, we're using a lot of local OO, but very often the local classes inherit from globally defined classes so that we don't have to write certain functionalities over and over again..

 

 

 

 
EnnoWulff
Active Contributor
0 Kudos
what I’d like to do next is to somehow introduce a local class in the function module and to then – step-by-step – switch one form routine after the other to a method

Why don't you use a completely new class for this? As far as I can see there is nothing that stops you from using a class (e.g. CALL SCREEN).

If you use local classes in the function group, how will you call them? Or do you plan to code something like this:
FUNCTION z_abc.

lcl_application=>popup_to_ask_for_task( ).

ENDFUNCTION.

 
BaerbelWinkler
SAP Champion
SAP Champion
Hi Enno,

you mean, that instead of working with a function group and module to work with a global class which then gets called from the Badi?

For now my preference is to start from what I'm familiar with as a point of reference, i.e. a function module instead of a class and to also keep all the code in one place instead of "scattering it around" - esp. as this isn't anything which is likely to get used by other processes anytime soon. One of the reasons I started this as a function module with form routines is, that I had the relevant prototyped code lying around from a while back, when I first started to experiment with this in a sandbox system. Now, that I have that code working, I'm weighing my options to perhaps take one step in the direction of ABAP OO by "switching" from calling form routines to methods in a locally defined class in the function group - comparable to what I did for some report programs which now contain local classes.

The other somewhat restricting factor in all of this is, that I currently don't really do a lot of ABAP programming, even though I've been doing it (and PL/1 programming before that) for many years. This is another reason why I'd like to keep things as simple as possible even if it means that it's most likely not the best approach. For me, one step at a time is better than doing it in leaps and bounds. ?
EnnoWulff
Active Contributor
you mean, that instead of working with a function group and module to work with a global class which then gets called from the Badi?

exactly. The advantage is, that you can leave your function group completely unmodified.

Doing the most obvious thing (implementing local classes near to the function module) is not always the simpliest solution... 😉
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Well - and the next won't come as a big surprise - I haven't yet set up a global class from scratch so am a bit hesitant to actually do that, not really knowing what I'd have to do where and how. If, on the other hand, there's a fool-proof template around somewhere which lays out a generic framework into which the method definitions and implementations can easily be dropped in, I might give it a try.

 
EnnoWulff
Active Contributor
That easier than creating local classes! 😉

Start transaction SE24, enter a valid name like ZCL_WHATEVER_POPUP, give it a description and enter the methods (=PERFORMs) you need. Instead of PERFORM something you just write something( ) or CALL METHOD something.

Entering the methods and parameters in the form based SE24 editor is kind of fool-proof.
BaerbelWinkler
SAP Champion
SAP Champion
Sounds easy enough – but I have a hunch that the proverbial devil lies in the details! I’m for example passing structures for internal tables back and forth between the main logic and the form routines and rely on global definitions in the top-include for those. Due to the way we handle dictionary objects I avoid creating DDIC-definitions as much as I can and prefer to have the data- or type-definitions directly in the programs. Would these definitions simply need to be put into the public definitions of the global class to make this comparable or is that just not “allowed” even if it’s technically possible? And if the latter, what are the alternatives?
EnnoWulff
Active Contributor
yes! Exactly the way you described it.
nomssi
Active Contributor
Hi Bärbel,

I would consider the CRC approach to define local classes. Note I say classes, not a single local class. Just from your specification I would think of a USER and a TRANSPORT class.
I’m well aware that some options available with ABAP OO – like unit testing – 

I hope you are aware can write unit tests for your function module.

and, there is always a magic step.

JNN

 
joachim_gross
Product and Topic Expert
Product and Topic Expert

Hi,

just have in mind that the parameter-types are a bit different.

  • The using parameter does not prevent changing the value within the form, unlike the import parameter which may not be changed within a method.
  • Tables parameter are not allowed for methods. In newer systems you even get a warning message if you try to create them on forms or form routines stating them obsolete.
  • The changing parameters of a form together with the tables parameter could be matched to changing, exporting or even returning parameters.

But that makes them a lot more expressive and cleaner.

Regards;

Joachim

 

PS: for one of my personal projects i made a matching table:

Assuming we use an interface and no static methods.

FromTo
Function Group ComponentClass Component
Function ModulesInterface Instance Methods
Form RoutinesPrivate Instance Methods
Events (LOP)
Global VariablesPrivate Instance Attributes
Constants / TypesInterface constants and types
MacrosMacros
Local test classes in *T99-includeLocal test class

 

for parameters

FromTo
Function moduleForm routineMethod
 UsingImporting
TablesTablesChanging
Exceptions Exceptions
Changing Changing
 ChangingExporting
Importing Importing
RaisingRaisingRaising
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Thanks, Joachim!

And, to show my woeful ignorance regarding (ABAP) OO, a question:

Why is a function module comparable to an "interface instance method"? Unfortunately, I still haven't really grasped what the interface construct actually means and when/why to make use of it. What I think I picked up is that an interface doesn't have any code just the parameters, so how is that comparable to a FM which obviously does have executable code? And, I'm not even sure if my question makes any kind of sense!
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Thanks, Jacques!

Never heard of the CRC approach but from a quick look it might be more to my liking than UML-diagrams. At a guess, it's something also doable e.g. via Mindmanager or a similar tool (yes, I prefer readable electronic versions to scribbled notes on cards/post-its).
"I hope you are aware can write unit tests for your function module."

Ummm, no not really, I'm afraid. And unit tests are not all that high on my agenda as I'm fairly certain that I have to first have a much better grasp of ABAP OO before tackling that in earnest. One (little) step after the other ....

Cheers

Bärbel
mlipnicki
Explorer
It's not 🙂

In all honesty, if it's your first dive into OO, forget about all the advanced concepts, UML diagrams, interfaces, inheritance...

Start simple, start with what you know ... and you'll probably like it.

As patrick.vannierop said: just do 🙂

At a very basic level:

  1. think of a class as a function group - it's there to group functions and attributes together

  2. think of public methods as function modules - anyone can call them and they can use stuff contained within a class/function group

  3. think of private methods as procedures/forms/performs - they're private = you can call them inside the class/function group, but everyone "outside": hands off 🙂

  4. method parameters work exactly like function module parameters, except you can't use TABLES

  5. type definitions, class-wide/global attributes work just like in function groups


That's it 🙂

(it's actually how the class concept was first introduced in ABAP back in r/3 4.6c days; all the "advanced" stuff came later)

M.

P.S. Start with a global class, it's a lot easier to grasp than the syntax of local class definitions. You'll quickly get frustrated with the latter (I know I did).
joachim_gross
Product and Topic Expert
Product and Topic Expert

Hi,

commenting when having a bit of temperature is not that good. I thought too complex. My matching tables describe a mitigation strategy from function groups to classes.

Nearest to a function group is a static class with static methods.
But this approach has a lot of cons with regards to writing testable and robust code.
Therefore i always recommend not to use them but classes with private instatiation using at least one interface and and for each at least one factory method. But as i see in the comment from Michal, these are already advanced concepts.

First of all, compare function modules to public methods and forms to private methods.
But here we have the first difference. Unlike forms private methods are really private (if we ignore friends) . Forms can be called from everywhere with “perform … of program …”. Not a good coding style but possible.

Global Variables of a function group could be compared to attributes of a class. Here the same: private attributes are really private unlike variables which can be changed from outside via ‘assign’. 

Typing: In form routines parameters can be untyped. This is not allowed in methods.

Parameter naming: In function modules it is possible to name an importing parameter exactly the same as an exporting parameter. This is not allowed in methods.

At last: i second the recommendation to start with global class. It really makes starting with ABAP-OO a lot easier.

Hope i could clarify some parts.

Joachim

 

Postscriptum: I love to recommend the paper of Blumenthal/Keller allthough some code examples are a bit outdated now
http://www.sapwiki.cl/wiki/images/9/90/Abap_top_part_2.pdf

 

Postscriptum 2: Regarding Interfaces, Singleton etc.

the following code snippets are not tested in SE80 or ABAP in Eclipse, so they may content some syntax errors;-)

FUNCTION zjg_fm1_1.
*”———————————————————————-
*”*”Lokale Schnittstelle:
*”  IMPORTING
*”     REFERENCE(IV_GUID) TYPE  GUID_16 DEFAULT ‘1234567812345678’
*”     REFERENCE(IF_FLAG) TYPE  CHAR_01
*”  TABLES
*”      RETURN STRUCTURE  BAPIRET2
*”———————————————————————-

could become

interface ZJG_IF_NEW_CLASS
  public .

  types:
    TT_BAPIRET2 type standard table of BAPIRET2 with default key .

  methods ZJG_FM1_1
    importing
      !IF_FLAG type CHAR_01
      !IV_GUID type GUID_16 default ‘1234567812345678’
    changing
      !RETURN type TT_BAPIRET2.

endinterface.

So you see that the interface contains the definition of the method and a type needed for its signature.

The class ZJG_CL_NEW_CLASS would implement the interface

class zjg_cl_new_class definition create private.

public section.

interfaces zjg_if_new_class.

class-methods s_get_instance returning value(rr_instance) type ref to zjg_if_new_class. 

private section.

class-data mr_singleton_instance type ref to zjg_cl_new_class.

endclass.

 

class zjg_cl_new_class implementation.

method s_get_instance.

  if mr_singleton_instance is not bound.

    mr_singleton_instance = new zjg_cl_new_class( ).

  endif.

  rr_instance = mr_singleton_instance.

endmethod.

method zjg_if_new_class~zjg_fm1_1.

  “import the code of the function module and adapt it to the stricter rules of ABAP-OO.

endmethod.

endclass. 

 

A simple call could be 

data lr_instance type ref to zjg_if_new_class.

data lt_return type zjg_if_new_class=>tt_bapiret2.

lr_instance = zjg_cl_new_class=>s_get_instance( ).

lr_instance->zjg_fm1_1( exporting IF_FLAG = ‘A’
                                                       IV_GUID  =  ‘1222567812225678’
                                        changing RETURN = lt_return).

There are shorter ways to do this using method chaining and inline declaration but let us start with this.

ascm
Explorer
0 Kudos
If you implement all in a global class, you can define structures or table definitions inside the class!

E.g.

 

class zi_cl_request_check definition
public
create public .

public section.

types: tt_contracts type standard table of ever with default key.

 
ascm
Explorer
0 Kudos
Nice help for first OO-Steps.

One Comment.

methods ZJG_FM1_1
importing
!IF_FLAG type CHAR_01
!IV_GUID type GUID_16 default ‘1234567812345678’
changing
!RETURN type TT_BAPIRET2.

 

Try to design your methods as single action like "get_username". Then you're able to return the values vie "returning ..."

methods ZJG_FM1_1
importing I_FLAG type CHAR_01
I_GUID type GUID_16 default ‘1234567812345678’
returning value(r_RETURN ) type TT_BAPIRET2.

 

Yes,  it's a diffrent behaviour then using changing, but's for OO it's the better style. Oh and try to forget exporting. Single value = returning.

 
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Thanks, Michal, that's helpful to put things into perspective!
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Thanks, Joachim! It'll take a bit of time to digest this. And - believe it or not - once upon a time (in a galaxy far far away ....) and if I remember correctly, I happened upon that SAPWIKI-article and may even have printed it out and read it. At least, it does look vaguely familiar, so I must have seen it before. Unfortunately, seeing it does not equate understanding it ? - but that would be way too easy anyway, wouldn't?!?
joachim_gross
Product and Topic Expert
Product and Topic Expert
0 Kudos

I would have done that, as i prefer functional methods since the very beginning.
For two years i follow the path to RESTful ABAP.

But the tables parameter of a function module / form is unspecified.

As Michal said exporting, importing and changing are very much the same i tried to stress my demo code with the change of a tables parameter.

So to keep the same functionality it had to be a changing parameter.

Joachim

 

BTW exporting has its advantages over returning in terms of performance.

But as it is written somewhere, performance improvements need measurements and should never be done in advance.

Personally i would check as you said if a returning is possible so one could then write:

data(lt_return) =
              zjg_cl_new_class=>s_get_instance( )->zjg_fm1_1( exporting                                                                                                                 IF_FLAG = ‘A’
                                                                                   IV_GUID  =  ‘1222567812225678’ ).

Or better having a separate private method in the caller class for the instantiation. As shown in OpenSAP course https://open.sap.com/courses/wtc1/overview.  

joachim_gross
Product and Topic Expert
Product and Topic Expert
0 Kudos

I too found a printout of the predecessor of that article yesterday while packing for an office move. Wow, written in 2005.

BaerbelWinkler
SAP Champion
SAP Champion
Thanks for all the comments thus far!

Let me try to summarise the gist of what I should do next based on your recommendations and my understanding of them:

  • create a global class instead of trying to add a local class to the function group/module

  • make the global class static (at least to begin with) - for the intended purpose it will just be called once from the Badi, so no instances are needed anyway as far as I can tell

  • create one public method with basically the same interface I have for the function module (transport type, username and transport title as importing parameters and the updated transport title as a returning parameter)

  • copy and paste the main logic from the function module into this public method

  • copy and paste the content (ie. the definitions) from the top include into the private section of the class definition (not changing their names for now)

  • create one private method for each form routine currently called either from the main processing or one of the form routines

  • use the same importing parameters but make sure to always just have one returning parameter (this should be doable at least for most if not all of the routines)


Once that is done and works, I'll have to take a close look at the logic then contained in the methods as most of them will most definitely be doing a lot more than just one thing! Unfortunately, the form-routines became larger than I had planned due to the different combinations I needed to be able to tackle to check and build the transport title. My hope is that refactoring done via Eclipse will help with that so I'll wait with that until after the global class produces the same results as the function module.

So, how viable a plan is this as far as the next steps are concerned?

Cheers

Baerbel
matt
Active Contributor
0 Kudos
I shied away from local classes for years because global classes are so much easier to develop - if you use classic SE24 in SAPGUI.

Now I use local classes widely.

 
matt
Active Contributor
0 Kudos

  • create a global class instead of trying to add a local class to the function group/module


Yes. This will make life easier (if using SAPGui SE24 ). Furthermore, you're separating completely your class from the function group. This will prevent you taking shortcuts with function group global variables etc.

  • make the global class static (at least to begin with) – for the intended purpose it will just be called once from the Badi, so no instances are needed anyway as far as I can tell


No. From the off get used to programming with instances. I know people who've started with just statics, and in my opinion it hindered them from learning to think in an OO manner. There are some things that are good OO practice that you an easily move into later (like creating an interface, then implementing) - but modifying a class from static to instance is a pain.

 

The rest of your proposals look good.

By the way, this is what my implementation of CHECK_BEFORE_RELEASE looks like.
METHOD if_ex_cts_request_check~check_before_release .

DATA(checker) = NEW zcl_request_checker( request ).

checker->check( i_text = text i_attributes = attributes i_keys = keys ).

IF checker->has_errors( ) EQ abap_true.
checker->display_errors( ).
RAISE cancel.
ENDIF.

IF checker->has_warnings( ) EQ abap_true.
checker->display_warnings( ).
ENDIF.

ENDMETHOD.

 

 

 

 
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Thanks, Matt!

I thought that I'd be further off the mark with my bullet list so it's somewhat reassuring  that this isn't the case.

My rough idea for calling the check logic is to only do it under specific circumstances like when it's a workbench request and when it's invoked in an online and not a batch process. The check logic will always return a transport title containing the required information - until it does, there's no way to back out of the pop-up logic and therefore FM. This is by design as we've tried long enough with asking people nicely to please adhere to the guidelines to not much avail.

Cheers

Baerbel
michal_lipnicki
Explorer
0 Kudos
Sounds like a plan! Sweet and simple, exactly as it should be: KISS and YAGNI are there for a reason.

However, I do agree with matthew.billingham - static classes are the root of all evil 🙂

Do let us know how it turned out! Another blog maybe?
BaerbelWinkler
SAP Champion
SAP Champion
Hmmm, why the caveat "if you use classic SE24 in SAPGUI", Matt? From many other comments, I take it that "we" should actually have been using ABAP in Eclipse for basically ages, so I'm a bit confused (as I'm currently forcing myself to use Eclipse at least for ABAP OO development).
matt
Active Contributor
0 Kudos
Just that the form based editor of se24 is probably easier at first.
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Actually, one of the first things I did when it became available was to switch to "source code based editor" as I prefer to see the complete code, use search via <CTRL>+F and be able to scroll up and down at will, just like with procdedural code or local classes (as long as that doesn't work with includes). ?

 
matt
Active Contributor
0 Kudos
When I started with ABAP Objects, there wasn't any source code based editor! If you can use it from the start, that's great!
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Here is another update:

I haven't yet embarked on this "journey" as I decided to first work my way through the recently started blog series "Getting comfortable using the Object Oriented design model with ABAP" by jjamese.mcdonough.

michal.lipnicki - I'm fairly certain that there'll eventually be a blog post (or "travel log") as an outcome of this adventure. I just don't yet know where it'll take me or how long it'll take?

Cheers

Baerbel

 
joachim_gross
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Bärbel, i just want to give you  some more input for your journey:

https://blogs.sap.com/2018/07/26/migration-from-function-groups-to-abap-oo-artifacts-the-manual-way-...

Regards;

Joachim
BaerbelWinkler
SAP Champion
SAP Champion
0 Kudos
Thanks, Joachim! I already noticed your series and started to read the first part. To not get overwhelmed and in turn confused, I'll however first try to work my way through James E. McDonough's step by step guide mentioned upthread.

Cheers

Bärbel
ascm
Explorer
0 Kudos
Or better having a separate private method in the caller class for the instantiation

I use both technics. Depends a bit on the definition of the class and the methodes. There ist no wrong or right.

 

Hint: If your result of a method is a bigger table use a public (read-only) table in the class. Another way is to use a class-instance as data container. For the returning parameter is it in this way only a pointer.
Labels in this area