State Machine Part 2 : Team Procedural vs. Team OO

Spider in Canberra Close Up.jpg

Contents

Introduction

Opening Song by Team Procedural

Code Examples

Various OO and Procedural Musings

Closing Song by Team OO

Introduction

When Sumanth Kristam wrote the following blog:-

http://scn.sap.com/community/abap/blog/2014/01/09/classical-way-to-abap-oo-style-of-coding

Little did he realise what a can of worms he was opening up. The SCN programming world went crazy in a debate on the usage of OO programming. It’s pointless me recapping the vast array of comments but in essence “Team OO” were espousing the benefits of OO programming in changing existing programs, and “Team Procedural” were arguing it’s overkill for small programs, and if it’s so good can we have some examples please?

Can’t Get There From Here

In some of the blogs I have written on this subject I noted the nature of a lot of articles about OO programming I had read. If I can use a beer analogy, it is as if I had been drinking in the same pub for years, lets’ call it the “Procedural Arms” and then I hear about a wonderful new pub called the “Hero of OO”.

What is so good about this new pub? I hear myself ask.

Back comes the answer – “to get there you leave the current pub, turn left, go up George Street till you come to the Argyle Cut, go left again, under the tunnel, turn right at the end, and at the end of Lower Fort Street is the new, better, pub.

I don’t know if you’ve noticed, but that did not actually answer my question. The reply told me HOW to get there, but not what was good about the destination. The OO articles were all like that, it’s good because it’s good, and here is how you write the code technically.

That is why I started my own investigations. I have read a whole bunch of books and articles on the subject – the ones I recommend are “Clean Code” by Robert Martin and “Head First Design patterns” by Elisabeth and Eric Freeman. The latter in particular is chock filled with the sort of examples I was looking for.

I Still Haven’t Found What I’m Looking For

However all the examples are in Java or C#, which is not surprising as my understanding is that for every ABAP programmer there are twenty million who program in those languages? Nonetheless SAP is my thing, so I started a series of blogs where I translated those examples into ABAP.

I was merrily experimenting with assorted design patterns in ABAP, latterly the “state pattern”

http://scn.sap.com/community/abap/blog/2013/03/02/oo-design-patterns–decorator–in-abap

http://scn.sap.com/community/abap/blog/2013/01/08/domain-specific-language-in-abap

http://scn.sap.com/community/abap/blog/2014/01/15/enemy-of-the-state

I had got as far as building a bunch of tiny Z classes to implement a state pattern in ABAP, and I was going to see how easy it was to re-use that framework in another context. All OO – naturally. Then all hell broke loose in the Procedural/OO world.

So, I thought, I’ll re-write the “Gothic” example in a procedural way, and make sure it does the exact same thing as its OO equivalent. Then we can compare the two programs and that may tell us something.

Then (in the next episode) we will pretend the user has some extra requirements, and see how easy it is to change the two sorts of programs. An OO person would say the answer is obvious; OO is going to win this contest hands down. Someone from team procedural would say this example is such a small program, that the overcomplicated OO nonsense will make it more difficult to change.

Well, we shall see, will we not?

Without further ado, let’s get going. To make this more like an X Factor contest between the two styles I’ll let each programming style sing a song, one at the start one at the end. Since procedural programming predated OO programming it can go first. Most of the audience will be too young to remember “Eagle Rock” but here goes anyway.

Opening Song

Procedural Rock – Daddy Cool (1971)

Daw daw daw, daw daw daw daw,

Daw daw daw, daw daw daw daw,

Now listen,
Oh we’re steppin’ out.
I’m gonna write everything,
Gonna write everything twice and we’ll do procedural code.
Oh momma!
Oh you’re coding well!
Hmm – global variable,
Well we do it so well when we do procedural code.
Now momma,
Yeah you’re so rude!
Why don’t you give me a SHARED INCLUDE?
Hmm just give me that and we’ll do procedural code.

Chorus:
Hey Hey Hey good old procedural is here to stay,
We have always done it this way,
Doin’ procedural rocks.
Oh-oh-oh it’s so fast, OO’s so slow
I’m just crazy ’bout how fast we go
Doin’ procedural rocks.

Go momma!
Well you’re so keen!
Why don’t you give me a FORM ROUTINE?
Just gotta give me that and we’ll do procedural code.
Oh baby!
END-OF-SELECTION
You know that gives me, well I don’t like to say,

But anyway, we’re doin’ procedural code.

-Chorus-

-SOLO-

Now listen,
More we’re steppin’ out.
Yeah, gonna write everything,
Gonna write everything twice and we’ll do procedural code.

-Chorus-

-Outro-
Doin’ procedural rocks.
Doin’ procedural rocks.
Doin’ procedural rocks.

Return to Eden

For some years now, I have been forcing myself to do everything in an OO manner, every single program, even ALV reports (more on that later). I have a gut feeling that this will pay off in the long term.

However, it’s sad to say, but going back to writing a procedural program after several years of abstaining seems rather like unchaining yourself from a madman and having a hundred ton weight lifted off your shoulders. Header lines, global variables. You know it’s bad for you, but it feels so good.

Once again I can write a program from start to finish and it is an indescribable pleasure being able to write a call to a FORM routine, and then double clicking on that call and having the skeleton generated for you, which is something local classes can only dream of. That is why people have resorted to doing everything in Z classes, even for things which are obviously only ever going to be related to the application at hand.

This even gets unlikely support from part of “Clean Code” where Bob Martin says that a program should read like a newspaper i.e. the bit at the top of the source code should say in general terms what the program does, then as you read down it gets more detailed. This is fairly easy in the OO world of Java where you can declare things just before they are used, but ABAP rules enforce declaring everything before you do the implementations, which sort of ruins this.

In ABAP procedural code things do tend to look more like a newspaper, with typically a few routines after START-OF-SELECTION describing the various parts of the program, unless the writer had not modularised it at all.

Brahms and Code Listing

In the links to my blogs above you can see the code for the OO version of the “Gothic” program. In the “Domain Specific Language” one the code is at first glance longer than in the “state” one, because in the “state” one I have moved the bulk of the code into tiny Z classes.

Here is the procedural version. I have several comments to make:-

·         I kept in the unit tests, which of course need at least one class to work. This was needed as the test program cannot run on its own; the unit test is the only way to run it.

·         The unit test also proves the procedural code does the exact same thing as the OO code (as the test is exactly the same)

·         This also proves you can do unit tests for procedural programs just as easily as for OO ones. Even if you are dead against moving to OO I would urge you to start using the unit test framework as a bare minimum.

*&———————————————————————*
*& Report  Y_GOTHIC_PROCEDURAL
*&
*&———————————————————————*
*& ABAP implementation of Java example program written by Martin Fowler
*& in his book on Domain Specific Languages.
*& I did an OO implementation, now let us try to do the same using
*& procedural programming to contribute to the fierce debate on the
*& internet
*&———————————————————————*
REPORT  y_gothic_procedural.
*——————————————————————–*
* Types
*——————————————————————–*
TYPES: BEGIN OF g_typ_transitions,
         source_state      
TYPE string,
         event_name        
TYPE string,
         target_state      
TYPE string,
      
END OF g_typ_transitions.

TYPES: BEGIN OF g_typ_abstract_event,
         event_name
TYPE string,
         event_code
TYPE char04,
      
END OF   g_typ_abstract_event.

TYPES: BEGIN OF g_typ_state,
         state
TYPE string,
      
END OF g_typ_state.

TYPES: BEGIN OF g_typ_actions,
         new_state
TYPE string,
         command  
TYPE string,
      
END OF g_typ_actions.
*——————————————————————–*
* Lovely Global Variables
*——————————————————————–*
DATA: gd_current_state TYPE string VALUE ‘idle’,
      gt_events       
TYPE STANDARD TABLE OF g_typ_abstract_event,
      gs_events       
TYPE                   g_typ_abstract_event,
      gt_commands     
TYPE STANDARD TABLE OF g_typ_abstract_event,
      gs_commands     
TYPE                   g_typ_abstract_event,
      gt_states       
TYPE STANDARD TABLE OF g_typ_state,
      gs_states       
TYPE                   g_typ_state,
      gt_transitions  
TYPE STANDARD TABLE OF g_typ_transitions,
      gs_transitions  
TYPE                   g_typ_transitions,
      gt_actions      
TYPE STANDARD TABLE OF g_typ_actions,
      gs_actions      
TYPE                   g_typ_actions.

*——————————————————————–*
* Macros
*——————————————————————–*
DEFINE possible_events_are.
 
clear gs_events.
  gs_events
event_name = &1.
  gs_events
event_code = &2.
 
append gs_events to gt_events.
END-OF-DEFINITION.

DEFINE possible_commands_are.
 
clear gs_commands.
  gs_commands
event_name = &1.
  gs_commands
event_code = &2.
 
append gs_commands to gt_commands.
END-OF-DEFINITION.

DEFINE possible_states_are.
 
clear gs_states.
  gs_states
state = &1.
 
append gs_states to gt_states.
END-OF-DEFINITION.

DEFINE state_changes_after_event.
 
clear gs_transitions.
  gs_transitions
source_state = &1.
  gs_transitions
event_name   = &2.
  gs_transitions
target_state = &3.
 
append gs_transitions to gt_transitions.
END-OF-DEFINITION.

DEFINE state_reached_sends_command.
 
clear gs_actions.
  gs_actions
new_state = &1.
  gs_actions
command   = &2.
 
append gs_actions to gt_actions.
END-OF-DEFINITION.

*——————————————————————–*
* Off we go!
*——————————————————————–*
START-OF-SELECTION.
 
PERFORM configure_security_system.
 
PERFORM run_security_system.

*——————————————————————–*
* FORM Routines
*——————————————————————–*
*&———————————————————————*
*&      Form  CONFIGURE_security_system
*&———————————————————————*
FORM configure_security_system .
* Events
  possible_events_are
:
 
‘door_was_closed’       ‘D1CL’,
 
‘drawer_was_opened’     ‘D2OP’,
 
‘light_was_switched_on’ ‘L1ON’,
 
‘door_was_opened’       ‘D1OP’,
 
‘panel_was_closed’      ‘PNCL’.

* Commands
  possible_commands_are
:
 
‘unlock_the_panel’ ‘PNUL’,
 
‘lock_the_panel’   ‘PNLK’,
 
‘lock_the_door’    ‘D1LK’,
 
‘unlock_the_door’  ‘D1UL’.

* States
  possible_states_are
:
 
‘idle’,
 
‘active’,
 
‘waiting_for_lightLight’,
 
‘waiting_for_drawer’,
 
‘panel_is_unlocked’.

* Behaviour
* Idle State
  state_reached_sends_command
: ‘idle’ ‘unlock_the_door’,
                              
‘idle’ ‘lock_the_panel’.
  state_changes_after_event
:   ‘idle’ ‘door_was_closed’ ‘active’.

* Active State
  state_changes_after_event
:

‘active’ ‘drawer_was_opened’     ‘waiting_for_light’,
‘active’ ‘light_was_switched_on’ ‘waiting_for_drawer’.
*
* Waiting for light State
  state_changes_after_event
:

‘waiting_for_light’ ‘light_was_switched_on’ ‘panel_is_unlocked’.

* Waiting for drawer State
  state_changes_after_event
:

‘waiting_for_drawer’ ‘drawer_was_unlocked’ ‘panel_is_unlocked’.

* Panel is Unlocked State
  state_reached_sends_command
: ‘panel_is_unlocked’ ‘unlock_the_panel’,
                              
‘panel_is_unlocked’ ‘lock_the_door’.
  state_changes_after_event
:   ‘panel_is_unlocked’ ‘panel_was_closed’ ‘idle’.

ENDFORM.                    ” CONFIGURE_security_system
*&———————————————————————*
*&      Form  RUN_security_system
*&———————————————————————*
FORM run_security_system .
* Local Variables
 
DATA: ld_code TYPE char04.

  EXIT.“To stop anybody actually running this test program!

  WHILE ld_code NE ‘STOP’.
   
PERFORM poll_for_event CHANGING ld_code.
   
PERFORM handle_event   USING    ld_code.
 
ENDWHILE.

ENDFORM.                    ” RUN_security_system
*&———————————————————————*
*&      Form  POLL_FOR_EVENT
*&———————————————————————*
FORM poll_for_event CHANGING pcd_code TYPE char04.
* Code to poll the external system to see what event has occurred
* Most likely a proxy call to PI
ENDFORM.                    ” POLL_FOR_EVENT
*&———————————————————————*
*&      Form  HANDLE_EVENT
*&———————————————————————*
FORM handle_event USING pud_event_code TYPE char04.
* Preconditions
 
CHECK pud_event_code IS NOT INITIAL.

  READ TABLE gt_events INTO gs_events WITH KEY event_code = pud_event_code.

  CHECK sysubrc = 0.

  PERFORM transition USING    gs_eventsevent_name
                    
CHANGING gd_current_state.

ENDFORM.                    ” HANDLE_EVENT
*&———————————————————————*
*&      Form  TRANSITION
*&———————————————————————*
FORM transition  USING    pud_event_name    TYPE string
                
CHANGING pcd_current_state TYPE string.

  READ TABLE gt_transitions INTO gs_transitions
 
WITH KEY source_state = pcd_current_state
           event_name  
= pud_event_name.

  CHECK sysubrc = 0.

  pcd_current_state = gs_transitionstarget_state.

  LOOP AT gt_actions INTO gs_actions

WHERE new_state = gs_transitionstarget_state.


   
READ TABLE gt_commands INTO

gs_commands WITH KEY event_name = gs_actionscommand.
   
CHECK sysubrc = 0.
   
PERFORM send_command_to_ext_system USING gs_commandsevent_code.
 
ENDLOOP.

ENDFORM.                    ” TRANSITION
*&———————————————————————*
*&      Form  SEND_COMMAND_TO_EXT_SYSTEM
*&———————————————————————*
FORM send_command_to_ext_system USING pud_event_code TYPE char04.
* Code to send out a message to be displayed by an external system
* Most likely a proxy call to PI
ENDFORM.                    ” SEND_COMMAND_TO_EXT_SYSTEM
**——————————————————————–*
** Local Class Implementations
**——————————————————————–*
CLASS lcl_test_class DEFINITION FOR TESTING
   RISK LEVEL HARMLESS
   DURATION SHORT
   FINAL
.
**——————————————————————–*
** Miss Grant has a secret compartment in her bedroom that is normally
** locked and concealed. To open it, she has to close the door, then
** open the second drawer in her chest and turn her bedside light
** on—in either order. Once these are done, the secret panel is unlocked
** for her to open.
**——————————————————————–*
 
PRIVATE SECTION.
   
METHODS: setup,
            
“IT SHOULD…………………
             open_panel_after_correct_steps
FOR TESTING.

ENDCLASS.“Test Class Definition

CLASS lcl_test_class IMPLEMENTATION.

  METHOD setup.
   
PERFORM configure_security_system.
 
ENDMETHOD.

*——————————————————————–*
* Actual Test Methods
*——————————————————————–*
 
METHOD open_panel_after_correct_steps.

    PERFORM given_user_in_room_door_open.

* WHEN user excutes the steps in the correct order
   
PERFORM when_door_gets_closed.
   
PERFORM when_second_drawer_is_opened.
   
PERFORM when_light_switched_on..

* THEN_secret_panel_is_open
    cl_abap_unit_assert
=>assert_equals( act = gd_current_state
                                       
exp = ‘panel_is_unlocked’ ).

  ENDMETHOD.

ENDCLASS.“Test Class Implementation
*&———————————————————————*
*&      Form  GIVEN_USER_IN_ROOM_DOOR_OPEN
*&———————————————————————*
FORM given_user_in_room_door_open .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = ‘door_was_opened’.
 
PERFORM handle_event USING gs_eventsevent_code.
ENDFORM.                    ” GIVEN_USER_IN_ROOM_DOOR_OPEN
*&———————————————————————*
*&      Form  WHEN_DOOR_GETS_CLOSED
*&———————————————————————*
FORM when_door_gets_closed .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = ‘door_was_closed’.
 
PERFORM handle_event USING gs_eventsevent_code.
ENDFORM.                    ” WHEN_DOOR_GETS_CLOSED
*&———————————————————————*
*&      Form  WHEN_SECOND_DRAWER_IS_OPENED
*&———————————————————————*
FORM when_second_drawer_is_opened .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = ‘drawer_was_opened’.
 
PERFORM handle_event USING gs_eventsevent_code.
ENDFORM.                    ” WHEN_SECOND_DRAWER_IS_OPENED
*&———————————————————————*
*&      Form  WHEN_LIGHT_SWITCHED_ON
*&———————————————————————*
FORM when_light_switched_on .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = ‘light_was_switched_on’.
 
PERFORM handle_event USING gs_eventsevent_code.
ENDFORM.                    ” WHEN_LIGHT_SWITCHED_ON

Really I think that is pretty self-explanatory, especially if you read the preceding blogs first. It has to be said that it does read more like a newspaper with each FORM pointing to the more detailed ones that come after it.

Naturally if you have not read the preceding blogs you will wonder what in the world this example is trying to achieve – in a nutshell the idea is to try and isolate the part of the program that says what it does from the code that does what it does, and have the “configuration” part in a natural language.

The unit test code is pretty much exactly the same length in both programs, as is the configuration section that demonstrates the “domain specific language” example.

The code that actually does the logic is a lot shorter in the procedural version. If I could bring myself to start using header lines again then the procedural code would be shorter still, but header lines have been burned out of me after all this time.

The funny thing is, in the arguments that rage, a lot of the time people are accused of writing s program using classes and methods that is REALLY a procedural program in wolf’s clothing – static classes and methods, methods that wrap function modules, public variables that are in effect global variables etc….

In my example I have gone the other way, and am trying to write a procedural program that mimics OO as much as it can. This could be described as trying to shove a square peg into a round hole.

Leaving that aside and getting back to the arguments, the problem with a fair comparison is this – if you do a really complicated example lots of people will get washed away by the complexity of it, or get bogged down in the complexity of the code, ignoring the point you are trying to make.

On the B side, the simpler the example, the more striking the percentage difference between the lines of OO code needed to do something and the procedural equivalent. For a small program the procedural version will be a lot shorter. As the complexity increases the sizes will slowly equalise. I am currently writing the most complicated application I have ever written in my life, it’s also by far the biggest in terms of lines of code.

I am doing this 100% in an OO fashion, following all the guidelines in the textbooks and from SCN, and I think it has reached the stage where it is smaller than if I had written it procedurally. I think.

The acid test of course, is how a program stands up to the non-stop stream of user requests. I have been with my company for 23 years, so I have followed the progress of some custom ABAP programs for a long while. I have found there are two things that can happen to a custom program:-

·         It stops getting used

·         It gets a non-stop stream of change requests, forever. Even the simple ALV report type ones.

This is why I truly believe that the bulk of programming work is enhancing and changing existing programs as opposed to creating new ones. I don’t think the figure of 90% is an exaggeration, at least in my experience, and I completely refute the argument that if your company introduces a new flavour of ice cream or the government changes a law that somehow magically makes your existing programs badly written.

Thus, as far as I can see the OO / Procedural argument stands or falls on how easy it is to change existing programs. Now is the time to jump down a rabbit hole.

The OO Empire Strikes Back

At the start of the blog it may appear I was bagging OO programming, so let’s have a bit of balance and here are the good things I have found about it.

In the examples above in the OO version the external system is encapsulated in its own class, and can be swapped out for a “stub” or whatever you want to call it very easily. The same thing is possible in procedural world I am sure, but it would not be as easy. You would have to have the external system encapsulated in a function module, withsome sort of global variable saying if a unit test was running or not. Then the unit test framework would have to call a function module of the same function group to set that global variable, before testing the productive code. That is not quite as elegant as the OO method, ad it means any old program could call the function that said “this is a unit test” and stuff the productive code of the program under test.

In addition, if we changed the external system in the OO version you could just pass in a new class with the same interface, I procedural world you would have to do some conditional logic in the function module that handles the external system. I have done this very thing in real life and it is HORRIBLE.

Stock Optional

What I also like about OO methods is that the parameters can be optional. To be fair that is true of function modules as well, but a function module is NOT a method, even if it looks a bit like one, and naturally FORM routines cannot have optional parameters.

This could mean having to have two FORM routines with different signatures, or doing what I used to do and passing in dummy values to the parameters you do not need, usually the CHANGING parameters. That is like in some standard SAP function modules where the TABLES parameter is compulsory, so you have to declare a table, and then include it in the function module call, even if you do not care at all about the contents.

I also like saying what the parameters are when calling a subroutine which you have to do in a method call but cannot when calling a FORM routine. This makes it almost impossible to pass in the values in the wrong order. If you make sure each parameter in a FORM routine has a TYPE then the problem is almost solved, unless you have two parameters in a row with the same type, then the caller could get them the wrong way around.

My Perfect Recursion

In one of my blogs the other day I described the problem of having a function where you had a random number of parameters, and how normally you have to guess the maximum number, and then declare ten optional parameters.

Using ABAP OO I created a class which took an instance of itself as an optional parameter, and then “unpacked” itself inside the method. That way you could pass in any number of parameters you wanted.

  mo_logger->add_db_read_log_entry(
    io_input_parameter 
= zcl_bc_parameter=>get( value = slump_group
                 predr 
= zcl_bc_parameter=>get( value = id_consistence_type
                 predr 
= zcl_bc_parameter=>get( value = id_sp_slump ) ) )
    id_database_table  
= ‘SLUMP_TABLE’
    io_output_parameter
= zcl_bc_parameter=>get( value = ld_hwr_material
                 predr 
= zcl_bc_parameter=>get( value = ld_initial_dosage
                 predr 
= zcl_bc_parameter=>get( value = ld_hwr_dosage_uom ) ) ) ).

Passing an object as a parameter, especially to itself as used in the “decorator” pattern, enabes you to do things which are very difficult in procedural world.

A SALV for my Wounds

What most people do is ALV reports, so this is where a lot of the examples and questions focus in on. Sadly the simple ALV programs given bloat the code so much it tends to put people off.

Here is a different take on this – when I first moved to ECC 6.0 I thought the SALV model was great, as it transformed your internal table into ALV output without having to mess about with the field catalogue.

That bonus was offset by the fact that – compared to REUSE_ALV_GRID – doing things like hiding columns and renaming them and sorting and what have you became ten million times more difficult; you had to create objects all over the place.

That was problem number one – problem number two was that I wanted to be able to add my own commands to the STAYS programmatically – like I could do with CL_GUI_ALV_GRID – and also make the grid editable if I so desired – like I could with CL_GUI_ALV_GRID.

SALV is not supposed to be editable at all I think – see my earlier blogs for how I got round that – but sadly I could do one or the other. I can either add my own commands programmatically to SALV OR make it editable but not both. So if I want both I need to use CL_GUI_ALV_GRID.

So, let’s say I have a report that is not editable, with my won commands. So I use SALV. Then a requirement comes along to make it editable – sometimes, for some users. Do I have to rewrite the whole thing totally?

REUSE_ALV_GRID, CL_GUI_ALV_GRID and CL_SALV_TABLE all do more or less the exact same thing, but have totally different “interfaces” i.e. how the calling program tells them to sort things or hide fields or what have you. This is a case for the “adapter” pattern.

/wp-content/uploads/2014/01/image002_375012.jpg

I then have a SALV_VIEW class that implements that interface and a GUI_ALV_GRID class that implements that interface, and at runtime my calling program decides which one to use, the rest of the calling program commands – to sort or hide a column or whatever, are the same in both cases. In the case of the SALV class it has all the million objects as attributes and handles the creation of them itself, leaving the calling program free to just say what it wants the display to look like. I also did a class for REUSE_ALV_GRID just because I could. I stopped short of doing one for WRITE statements but I could have. I should really do a WEB DYNPRO one as well. The point is this is future proof.

The Sixth Data Element

A common requirement in custom programs is to take a value of, say, a sales organisation (VKORG) and turn this into the text name for display to a human. The problem is that SAP has organised the text tables for every single data element differently, and some like T001W have the text description in the same place as the real table.

So, all across the programs you spend time working out how to get the text name of assorted things. I have a ZCL_BC_DATA_ELEMENT class (with a static main method and an abstract method) where you pass in the variable under question e.g. LD_VKORG and get back a string to LD_SALES_ORG_NAME. The class using run time type identification to work out what data element we are dealing with then uses the standard method to try and return the text description. If it finds a non-standard text table, then it looks to see if a subclass of itself exists with the data element in question in its name, and if so, creates an instance of that subclass and calls the main method again, thus getting the text description for a WERKS_D or a LIFNR.

In procedural world you could use function modules, but would have to copy more code for each new one you created, and load more into memory (I think) each time the main function module is called the whole group gets loaded into memory, whereas with classes only the static class and it’s subclass would be in memory (I think).

Ant Colony Fragile

This is probably not relevant at all, but since when has that stopped me.  The other day I was talking about that “anti-fragile” blog I had read on the SCN and was thinking about how to employ that in my daily work.

The idea is that every time you are given a change request, you think – can this change happen again, and if so, how can I make it easier to handle next time?

In this case the change request was to add a field to a DYNPRO pop-up box screen. That was easy, but then the extra field made the screen bigger, and scroll bars appeared and the user ad to scroll down to the field at the bottom of the screen, making them unhappy.

What I should have done was change the code of the CALL SCREEN 1234 STARTING AT X Y EDING AT A B such that it reflected the increased size. I did that and then thought – hang on, what happens when another field is added? Either myself, or another programmer will probably just change the screen and forget about changing the calling statement, especially if the screen is called from multiple places. So I wrote myself a little class to dynamically read the size of the screen from the database (table D020S).

METHOD enter_manual_weights.
* Local Variables
   
DATA: ld_end_at_width  TYPE d020snoco,
          ld_end_at_height
TYPE d020snoli.

    zcl_bc_screen_size=>get_pop_up_end_at_points(
     
EXPORTING
        id_program      
= syrepid    ” Program
        id_screen       
= ‘0700’      ” Current Screen Number
     
IMPORTING
        ed_end_at_height
= ld_end_at_height   ” End at Height
        ed_end_at_width 
= ld_end_at_width ). ” End at Width

    CALL SCREEN ‘0700’  “Enter Manual Weights
        
STARTING AT 25 06 “Same as Popup to Confirm
         ENDING  
AT ld_end_at_width ld_end_at_height.

  ENDMETHOD.“Enter Manual Weights

The aim of the game is that every change you make should make the next change easier, which is the opposite of what we are used to, but as can be seen from this example, more than possible. That is not a good entry in the procedural vs ABAP debate, as a function module could do the exact same thing, but I think this “anti-fragile” thing is just as important as unit tests, so I put it in anyway.

In the next exciting episode….

I think that is enough random burbling for the time being, in the next episode I will invent some new user requirements for my “Gothic” program and see if the OO version or the procedural version is easier to change.

Incidentally, that picture of the spider at the start of the blog I took last weekend, when I was at my friend’s house near Canberra. That is not a particularly dangerous spider (by Australian standards) but it was big enough to bother my wife, especially when my friend poked it. She didn’t realise he is a lawyer, so deadly spiders are scared of him, sadly they are not scared of computer programmers.

To finish off this instalment it is the turn of OO programming to sing a song….

Closing Song

OO Just A Little Bit – 1996 – Original Lyrics S.Tauber/S.Rodway

In the style of “The Wurzels”

Wurp idle didle do,

Wurp idle didle do,

You’re my code
You’re the sweetest thing
Don’t rot away
Don’t rot away

Every change makes me hate the users
Don’t Call Us
We’ll Call You

Is I wrong, to encapsulate
All me methods,

Be that pathetic?
I can’t hide all they patterns of state
(Oink!)
All my principles are open and closed

Baa!

CHORUS:

O! O!
Just a little bit
O! O!
A little bit more
O! O!
Just a little bit
Patterns by they Gang of Four

O! O!
Just a little bit
O! O!
Be just the job
O! O!
Just a little bit
Read they books by Uncle Bob

Moo!

Working out
With code that’s legacy
Inheritance

It makes me dance

But tonight that guy Feathers say
Let me refactor, whilst on me tractor
How can I prove my love for U  (ML)

Baby please, the subclass that I need
Is the Liskov substitution from hell

Ee-aww!

CHORUS:

O! O!
Just a little bit
O! O!
I feels the force
O! O!
Just a little bit
I loves that girl called Polly Morph
O! O!
Just a little bit
O! O!
A little bit more
O! O!
Just a little bit
Patterns by they Gang of Four

O! O!

O! O!

To report this post you need to login first.

16 Comments

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

    1. James Bungay

      I second Thomas’ commitment to purchase such a book.  I very much appreciate his courage to discuss this topic at such length and from a perspective that many ABAP developers continue to battle with.  Keep up the good work Paul!    

      Cheers,

      James

      (0) 
    2. Paul Hardy Post author

      Dear Tammy,

      That is good news, a very good start to the day. SCN will not let me send a message as she is not “following” me (that always sounds a bit creepy) but I found Kelly on Linked In and sent her a message.

      Cheersy Cheers

      Paul

      (0) 
  1. Eric Peterson

    I’m on team OO but I have to admit your code example is really good, and so readable.  That’s some delicious MACROroni and Cheese.  Looking forward to the next installments.

    (0) 
  2. Jelena Perfiljeva

    Shouldn’t have read it till the next installment came out – now I’m going to bite my nails waiting to find out who wins. Dammit! πŸ™‚

    In for one of the books as well (perhaps SAP Press should start taking pre-orders πŸ˜‰ ).

    (0) 
  3. Alejandro Bindi

    Had a good laugh at the songs, even without knowing the originals πŸ™‚

    A couple of comments:

    – With ZCL_BC_DATA_ELEMENT it seems you reinvented the wheel a bit as the class CL_TEXT_IDENTIFIER does exactly that. I still don’t get why this class isn’t more widespread as it’s so simple to use it, and shortens the code enormously not having to read text tables explicitly. I discovered it by chance a couple of years ago and been using it occasionally ever since with great results (it has a couple of bugs though, such as, you cannot use structures containing tables as they dump raise an exception (and the read fails) on an internally used DDIF function module).

    -The “ENDING AT” statement on CALL SCREEN is OPTIONAL! so if you just specify STARTING AT, the popup with be used to it’s full extent as defined in screen painter. So GET_POP_UP_END_AT_POINTS is not really needed (although a great idea πŸ˜‰ )

    Waiting eagerly for the next installment.

    Regards

    (0) 
    1. Paul Hardy Post author

      Hello!

      I had a look at the CL_TEXT_IDENTIFIER and it looks good.

      I pass in something like this to my Z class

      DATA: ld_vkorg               TYPE vbak-vkorg,

                 ld_sales_org_text TYPE string.

      ld_vkorg = ‘123’.

      zcl_bc_data_element=>read_text( EXPORTING id_value = ld_vkorg

                                                             IMPORTING ed_value = ld_sales_org_text )

      After the call LD_SALES_ORG_TEXT has the text name of sales organisation 123.

      This is the simplest possible interface I could find for the calling program. I am certainly going to add in CL_TEXT_IDENTIFIER behind the scenes now I know it exists. This is why I love feedback to blogs, this is a two way street and I learn a lot of things.

      As to why this class is not widely used – and why I had no idea it exists – is a subject I have raised before as to how hard it is to find standard SAP functions and classes that can be re-used. This is largely because there is no standard naming strategy for SAP objects, and often you get ten different versions of the same function / class.

      I’m not to proud to say I had no idea that ENDING AT was optional. When I started programming SAP all those years back I noticed all the other programmers always used ENDING AT and presumed it was just the way things were done.

      So it turns out the only purpose of using ENDING AT is to truncate a screen and force the users to scroll down or right…. the exact thing I was trying to avoid! Still, even if it was a rubbish example, the idea of making the program safer from future changes to popup schemes still stands, and in fact I can make the program in question more resistant to change by REMOVING lots of code (all the END ATS).

      So now I know two things I did not half an hour ago, and both will help me in my daily work … what a good day this is turning out to be!

      Cheersy Cheers

      Paul

      (0) 
      1. Alejandro Bindi

        Glad it helps!

        Indeed your Z class is simpler in that it needs no reference to Table / Structure and Field names. But maybe you can reuse that part of your class and leave the rest of the complexity to the standard one. The standard does allow for more complex scenarios, because it supports resolving texts on tables that have more than one key. In those cases instead of passing a single value, you pass a structured record with the needed keys, and in most cases the text gets solved as well. I can’t find my test case I did back then πŸ™

        But with the program RS_TEXT_IDENTIFY_TEXT you can test it extensively if I recall (not at the system now). More info here: Application-specific Enhancements (SAP Library – SAP Query (BC-SRV-QUE))

        I discovered both this class and the ENDING AT optionality NOT so long ago as well…and have almost 8 years on ABAP. So nothing to be ashamed of πŸ˜‰

        (0) 
      2. Bruno Esperança

        Yep, you just gotta LOVE the internet. I CANNOT imagine how people got by before it… I was lucky enough to get in the workforce well after internet was a basic need.

        Cheers and thanks for everything!

        Bruno

        (0) 
        1. Thomas Zloch

          It was all good. I think the English version of the saying is “what you don’t know won’t hurt you”, in German it rhymes “Was ich nicht weiß macht mich nicht heiß”.

          In 30 years they might wonder how mankind ever got by without implanted brain probes. 😈

          Thomas

          (0) 
          1. Bruno Esperança

            Well… from my school time I remember it was quite awful… each time we had an assignment about something… there I had to go and spend the whole day at the damn library πŸ˜€

            Funny, we also have that saying where I come from but it’s usually used for love affairs LOL πŸ™‚

            Best!

            Bruno

            (0) 
    2. Tomas Buryanek

      Thanks a lot! CL_TEXT_IDENTIFIER looks exactly like what I am looking for quite some time. Strange there is almost no info about it on internet .. (I mean class or function to return texts and descriptions of data types etc..).

      Paul:

      Great blog πŸ™‚ Can you please give more info about that “standard method” you wrote about? :

      “…then uses the standard method to try and return the text description…”

      (0) 
      1. Paul Hardy Post author

        To be more precise by “standard” I meant the base class method as opposed to an inherited  subclass method.

        I have a static method in my Z class called read text, where you pass in a variable of any type e.g. sales organisation, plant number, vendor number, whatever, and back comes the text description.

        Inside that method I use CL_ABAP_STRUCTUREDESC to find the domain of the variable passed in, and then I do assorted database reads to find the text table – a very complicated algorithim because every standard SAP data elemnt seems to have the text stored in a different wat, which I worked out myself.

        Now it turns out someon else had already done the hard yards in CL_TEXT_IDENTIFIER and I could have used that if i knew it had existed. Oh well, it was an interesting exercise anyway.

        (0) 

Leave a Reply