Skip to Content

The other day I was talking about exception handling and trying to get my head round how to use class based exceptions to make my programs better and achieve things that normal exception handling cannot.

Sadly, all of the examples I have seen concentrate on how class based exceptions work, which is fairly straightforward, as opposed to how they can be used to achieve new functionality. What I have been looking for is something along the lines of “when you have an error like THIS then with traditional exceptions you can only do THAT but with class based exceotions you can use the extra information / improved process flow options to do SOMETHING FAR BETTER “and suddenly everything is obvious.

When I started using ABAP Unit I found a raft of errors I would not have found otherwise within the first hour, so the added value was obvious from the start. If class based exceptions really are so much better then it should not be difficult to come up with an example scenario such as one I have described? You shouldn’t even have to think about it, if it’s that obvious?

Further to my investigations into the class based exception handling in ABAP, as I mentioned in my “Back to the Future” blog that I came across the – old in IT terms, from 1992 – concept of “design by contract” which ABAP has implemented in a way, and I believe Microsoft has implemented under the name “code contracts”.

http://se.ethz.ch/~meyer/publications/computer/contract.pdf

The idea is that in the signature of a method, as well as saying what input variables are mandatory for input and what ones are output, you also say what conditions at the start of a method (preconditions) would cause an error due to a software bug in the caller, and what conditions after the method has finished (post conditions) would cause an error due to a software bug in the method itself.

ABAP has the idea of ASSERTIONS which is the same thing, but in ABAP they just cause a dump or write to a log, and don’t raise an exception which can be reacted to, and you want that exception to be raised so that each program can decide how best to deal with the problem.

/wp-content/uploads/2012/09/image001_135920.png

Don’t take my example to seriously, they are just examples, in the case above the idea is that when the program reaches the ASSERT line the only way that condition will be true is if the program flow is doing something totally contrary to the way it is supposed to work.                       

In ABAP UNIT you can also test the conditions at the end (post conditions) for each routine, by starting a process which runs through each routine in isolation in test mode.

/wp-content/uploads/2012/09/image002_135921.png

However by their very nature these tests are isolated, so you check for errors in the caller, as the test IS the caller i.e. you are supplying the data manually. So, ABAP is almost there, but with gaps. I thought it would be nice to implement the “design by contract” thing exactly as it was intended, the way it was implemented in the original programming language EIFFEL.

The example from that language goes as follows:-

set_hour (a_hour: INTEGER)

           Set `hour’ to `a_hour

    require

        valid_argument: a_hour>= 0 and a_hour <= 23

    do

        hour := a_hour

    ensure

        hour_set: hour = a_hour

    end

If a silly value for the hour, like 99, is passed in then the caller is to blame, and that is where the exception should be directed, if the export parameter HOUR at the end of the routine has the wrong value then it is the fault of the routine itself, and that is where any exception should be directed. If either condition ever fails this indicates a bug in the program which should be corrected, so the exceptions raised are to deal with “impossible” situations, but as we know the impossible situations happen all the time.

In languages like JAVA you have extensions to the language built in all the time, as I understand it at the start of a Java program you list which libraries you are going to import, that is sort of like having to say at the start of an ABAP program which commands you want to use, a better analogy would be a string of INCLUDES each containing useful subroutines.

In ABAP OO world this becomes static classes, so you don’t have to declare them at the start, you just use them whenever you feel like it. So I created two exception classes ZCX_VIOLATED_PRECONDITION & ZCX_VIOLATED_POSTCONDITION both inheriting from CX_NO_CHECK on the grounds these are errors which should never happen. Interestingly I found some exception classes with almost the same name being used in the standard SAP HR programs.

Then I created a class ZCL_DBC (Design by Contract) to implement my two new ABAP commands with the same names as the EIFFEL commands above. Here is the result in my open item clearing program:-

*&———————————————————————*
*&      Form  UPDATE_CUSTOM_FIELDS
*&———————————————————————*
* Head office have a requirement to track which customer open items are
* automatically cleared.
* That is not as clear cut as you might think, but we pass in which
* transaction was used, and if the program was in batch at the time
*———————————————————————-*
FORM update_custom_fields USING pud_company      TYPE bsidbukrs
                                pud_customer    
TYPE bsidkunnr
                                pud_clearing_doc
TYPE bsidaugbl
                                put_belnr       
TYPE bkk_r_belnr.
* Local Variables
 
DATA: lf_in_batch TYPE abap_bool,
        ld_tcode   
TYPE sytcode VALUE ‘ZF32’,
        ld_subrc   
TYPE sysubrc.

* Preconditions – indicate a bug in calling routine
  zcl_dbc
=>require( id_that = ‘all cleared item key fields must be supplied’(081)
                    if_true
= boolc( pud_company      IS NOT INITIAL AND
                                    pud_customer    
IS NOT INITIAL AND
                                    pud_clearing_doc
IS NOT INITIAL AND
                                    put_belnr[]     
IS NOT INITIAL ) ).

* Routine Body
  “Do code that updates some Z data


  ld_subrc
= sysubrc.

  IF ld_subrc = 0.
   
COMMIT WORK.
 
ELSE.
   
ROLLBACK WORK.                                     “#EC CI_ROLLBACK
 
ENDIF.

* Postconditions – indicate a bug in this program
  zcl_dbc
=>ensure( id_that = ‘the Cleared Items database table will be updated’(080)
                   if_true
= boolc( ld_subrc = 0 ) ).

ENDFORM.                    ” UPDATE_CUSTOM_FIELDS

To translate back into English:-

This routine REQUIRES THAT all key item fields must be supplied

And in return

This routine ENSURES THAT the Cleared Items database table will be updated.

I am forcing a meaningful comment each time as it will be put into an error message whch wll make no sense unless the samenatic of the phrase are correct..

ZCL_DBC.jpg

EENSURE.jpg

That is the “contract” the routine has with any routine which wants to call it. If the pre-condition or post-condition fails then the corresponding exception is raised. How those “impossible” situationare dealt with is entirely up to the programmer.

The idea is also that as a by-product the routine is more self-documenting as you can see at the start more information of what is expected from the input parameters and at the end more information on what the routine was supposed to do. The document at that start of the routine should focus on WHY the routine is doing what it does rather than what it is doing.

You may ask why I am bothering when ABAP already has assertions and unit tests? To quote the Borg Queen “you see disparity where there is none”. I think this is complimenatry to existing tools. If I had a purely mathematical routine then a unit test is perfect for testing the post-condition. If I had a situation in the middle of a routine which I was 100% sure would never occur then an ASSERT statement may be the way to go. The “design by contract” thing is more for routines that do things like update the database which you never expect to fail and want to raise an exception at run-time to be handled as you see fit.

There is another concept called the “class invariant” – with a great analogy in the attached article about it not being mentioned in a cooks employment contract that he should not burn the restaurant down, but you expect him not to do that regardless, but god knows how you would do that in ABAP, other than calling the same general check at the end of every unit test method,

As always, feedback is more than welcome. Please don’t be afraid to tell me I am barking mad, I am doing all these experiments and reading all this background material just to try and get the most out of OO programming, and if I am going down the wrong path you would be doing me a favour steering me right.

I did think it is funny that in the SAP press book “ABAP Programming Guidelines” Horst Keller syas that ABAP Objects is easier to learn than procedural programming. Does anyone else out there feel that way?

P.S. On a totally different subject, I was in the SAP Service Marketplace the other day whilst changing a repair in our system into an enhancement, and the SAP netweaver version defaulted to a lower value than I am on, 710 instead of 711. So when I did the drop down I wondered what the highest version I could choose was. It turned out to be SAP netweaver version 804. As the service marketplace is used interanlly by SAP that explain why those values are there, I can only presume internally SAP are working on the next major release, even if it does not come out until 2020….

That’s it for now, local election day here in New South Wales, Australia. It is illegal not to vote in Australia, so I had better get down to the polling station rigth now, or I will get a fine.

Mit freundlichen Grüßen/Cheersy Cheers

Paul..

To report this post you need to login first.

3 Comments

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

  1. Michael Wegelin

    Thanks for sharing your thoughts, Paul.

    Yes, I think design by contract is a GREAT idea. As you state,

    The idea is that in the signature of a method, as well as saying what input variables are mandatory for input and what ones are output, you also say what conditions at the start of a method (preconditions) would cause an error due to a software bug in the caller, and what conditions after the method has finished (post conditions) would cause an error due to a software bug in the method itself.

    Now, with the class-based exception concept you can exactly do this. With the addition RAISING in the declaration of a form or a method or a function module you can tell the caller already in the signature, that he needs to be aware that you throw him exceptions into his face, if he does not call your procedure correctly. For this to work, however, you need to derive your exceptions (ZCX_VIOLATED_PRECONDITION; ZCX_VIOLATED_POSTCONDITION) not from CX_NO_CHECK, but rather from CX_STATIC_CHECK.

    If you use this inheritance path AND declare in the signature of your procedure with the RAISING addition, that those two exceptions might be raised, you can happily raise your exceptions from within your procedure.

    If you omit the RAISING addition in the signature, the compiler will complain, that the exception you raise needs to be handled or declared.

    If you inherit from CX_NO_CHECK, you will NOT be able to declare the exception to the caller in the signature of the procedure. If you try, the compiler will flag this as an error.

    So your idea of programming by contract is great plus you can even yous class-based exceptions derived from CX_STATIC_CHECK to implement your idea.

    Cheers,

    Michael

    (0) 
    1. Uwe Kunath

      As Michael mentioned, it is a great blog, One of the disadvantages of using CX_STATIC_CHECK as the base class for the exceptions, is the fact that you would be starting to litter exception declarations in all your enhanced methods. Sometimes it makes sense, sometimes not. Wouldn’t it be an option to have a parallel exception class hierarchy for checked exceptions and also have a parallel method set in ZCL_DBC which could be called ENSURE_CHECKED / REQUIRE_CHECKED (both of these methods would declare the checked versions of the exceptions).

      This gives the caller the flexibility it needs in order to decider weather or not the checked exception hierarchy is to be used.

      (0) 
  2. Paul Hardy Post author

    One of my main aims to to avoid declaring the same exception all over the place, that would just detract from the readability of the code.

    These are fatal errors we are talking about that should never occur i.e. they represent a serious bug in the program. I would ideally want the “culprit” i.e. the routine that is executing in the case of a postcondition, or the calling routine in the case of a precondition, to be sent to the application log for analysis by a programmer, and then a dump in development or test, or an error message in production to avoid puzzling the user.

    That being said, the idea of duplicate methods with exceptions inheriting from CX_STATIC_CHECK seems a reasonable idea, thus increasing the programmers options.

    Cheersy Cheers

    Paul

    (0) 

Leave a Reply