I’ve been away from coding for some time (on project management tasks) so I haven’t been able to write as much as I would like, but recently I had the time to do some coding and again I was reminded of the benefits of going object oriented. This time – encapsulation, something I indirectly wrote about in my previous blog (Why object oriented? – Class Attributes), but I feel it’s a topic that’s more generic and deserves its own entry.

To provide some context, I was doing some work related to material valuation, where I wanted to discover the mean average price for a given plant over the last few year considering only goods receipts and stock migrations.

I had an object for the material movement line item, and I has making a sum of all the values /quantities, and then calculating na average.  In terms of code I had something like this:


data(lt_movements) = ZCL_MOVEMENT_ITEM=>GET_ITEMS_WITH_FILTER( filter ).
Loop at lt_movements into data(lo_movement)
     lv_total_quantity = lv_total_quantity + lo_movement->get_quantity(  ).
     lv_total_value = lv_total_value + lo_movement->get_value(  ).
Endloop.
Lv_map = lv_total_value / lv_total_quantity.


While I was testing the program I discovered a bug related to STOs, where the value of the transfer is posted in the 641 not the 101. I had to change the GET_VALUE( ) method to take into consideration this logic.  If you extrapolate to a situation where the GET_VALUE( ) was used in multiple places you can easily see the benefit of encapsulation.

But why is this specific to object oriented programming? I can encapsulate in procedural programming too right? Yes you can, but with some drawbacks:



     1.   Too Verbose

The equivalent procedural code, just for one attribute would be something like:


   perform get_value_of_movement_item using lv_docnumber
                                                                                     lv_docyear
                                                                                     lv_docitem
                                                                  Changing lv_value.
  lv_total_value = lv_total_value + lv_value.







Not only does it take more work to write the code (and laziness takes over), it’s harder to read.

     2.    Lack of general rules

If you consider that GET_VALUE( ) only has (in the first version) a SELECT statement to MSEG, you can easily concluse that many programmers won’t bother creating a FORM just for the SELECT, they will write the SELECT directly in the LOOP (forget the FOR ALL ENTRIES discussion please, not the point).

You can then say “I create a FORM for every DB access”, but this is just one example. The GET_VALUE( ) could return a calculation between attributes of the object:  lv_value = me->quantity * me->unit_price. Don’t try to convince me that a procedural programmer would create a form just for a multiplication.

In Object Oriented Programming there are rules to enforce this, I don’t have to think:

  • Every characteristic of the object is accessed through a getter: This prevents me from putting the quantity * net_price outside my class. I use charateristic and not attribute to separate what is a formal attribute of the class and what is a characteristic of the movement line item. For example in my case, the value of the line item was not an attribute of the class;

  • Every DB access must be made inside the respective class: This prevents me from having a rogue SELECT * FROM MSEG somewhere in my code, instead of retrieving the value from the class via getter.

I don’t have to think if GET_VALUE( ) is only a SELECT or 100 lines of code, it has to exist according to OO rules, and the only way to get the value of the movement is through GET_VALUE( ) so there is only 1 point of access to  update.

Encapsulation is extremely important because things change, and like in my example, what in the beginning was simply a SELECT FROM MSEG, later changed into something that had to have a different behaviour for STOs.

PS: I know I take a hit in performance due to encapsulation, but having scalable and bug free code is a priority for most of the project I handle.

To report this post you need to login first.

36 Comments

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

  1. Shai Sinai

    Hi Joao,

    In general, I agree with your perspective.

    However, I must mention that your example may just prove the opposite since it demonstrates the main drawback of OO in SAP: SQL selections of mass data (which, in my opinion, cannot be handled efficiently in OO in current ABAP tools).

    If I understood correctly, you are executing selects from DB in a loop for a mass of records.

    This, of course, may lead to severe performance issues.

    If you ask me, this is the main reason that OO isn’t adopted widely as the one and only programming paradigm in ABAP.

    (0) 
    1. Joao Sousa Post author

      Hello,

      I can take a hit in performance, like I state in the last line, but I never found it a deal breaker.

      I don’t show in the code how I fill the LT_MOVEMENTS table (I’ll had that), but that is a mass SELECT in a static method of the movement line item class, a SELECT that can retrieve most of the data for all the objects in a single SELECT.

      I’m also not showing what is actually inside of GET_VALUE( ), and if you assume the internal value from MSEG was assigned (via setter) to an attribute of the class in the static method that created the objects, there isn’t the need for a SELECT SINGLE inside the GET_VALUE( ), and therefore no performance problems.

      Inside the GET_VALUE( ) I can still choose to use the value grabbed from MSEG or add additional logic (for the occasional STO), something I wouln’t be able to do if I had assigned the MSEG field directly in the loop.

      As I final remark I would accept that reason for not using OO if most programs I saw were clear examples of procedural best practices, but that is not the case. From my experience most of the time people don’t use OO because they don’t know it, and don’t know it because they don’t understand the advantages.

      (0) 
      1. Shai Sinai

        Well,

        I cannot tell what is the code behind your encapsulated methods, of course.

        However, if you are using a static method for the selection (not sure what is the exact flow, though) you are taking the edge off the OO concept.

        (0) 
        1. Joao Sousa Post author

          In what way am I taking the edge off the OO concept?

          I’m doing the DB selection to MSEG inside the line item class, creating objects and using getter/setter to fill values according to the database.

          I’m still going to use getters to retrieve the attributes later, not blindly retrieving the MSEG values, so the encapsulation is perserved. That’s the reason it’s important to use the getter/setter even inside the class methods, instead of ignoring “Private” and going for the me->attribute = ‘X’ or X = me->attribute.

          (0) 
          1. Shai Sinai

            So you perform select from DB for each item separately, then?

            I must confess I’m not sure I’ve followed your flow.

            As far as I can see, there are two options for implementation:

            1. Specifying all the document/line numbers in advance for single selection. This approach may require you a complex implementation or limits the separation of concerns.

            2. Performing separate DB selection for each item.

            (0) 
            1. Joao Sousa Post author

              To simplify assume that the class has private attributes for all fields of MSEG (not necessary but for simplicity)


              All the DB logic is inside ZCL_MOVEMENT_ITEM=>GET_ITEMS_WITH_FILTER( filter ) which contains a SELECT to the DB with the ranges defined in “filter” (i.e material, plant, movement type, etc).

              SELECT * FROM MSEG INTO LS_MSEG WHERE  A IN filter->S_X and B IN filter->S_Y and ….


                   CREATE OBJECT lo_item

                        Exporitng

                             mblnr = ls_mseg-mblnr

                             mjahr = ls_mseg-mjahr

                             zeile = ls_mseg-zeile.


                   lo_item->set_db_internal_value( ls_mseg-xxx).

                   lo_item->set_db_quantity( ls_mseg-yyy)

                   append lo_item to return.

              ENDSELECT.

                  

              One mass select. Multiple objects. GET_INTERNAL_VALUE( ) can return the DB internal value directly or add additional logic.


              Note that someone using the class doesn’t need knowledge of the DB structure behind it, for all they know the object could be instantiated using data retrieved from a webservice.


              (0) 
              1. Shai Sinai

                Thanks for the clarification.

                This should do the job.

                However, it works only for very simple cases.

                As long as you aren’t going to implement a separate class and/or get_instance method (GET_ITEMS_WITH_FILTER) for each and every business requirement it won’t handle any of the following:

                1. Multiple selections from different tables (where you want to use lazy evaluation and not to select all the data in advance).

                2. Combination of several business objects (i.e. complex joins).

                For (a very simple) example, select all material documents items of materials whose material group is X.

                (0) 
                1. Joao Sousa Post author

                  If performance is paramount then yes, you will write a specific method for each specific business requirement, the same reason you don’t expect a generic SELECT for every business requirement if performance is your ultimate objective.

                  As for 1, I don’t get what you are trying to do. If you want to get all POs and then all PO items, with lazy loading, you should use something like:

                  lt_items = Z_PO_ITEM=>GET_ITEMS_FOR_POS( lt_pos).

                  method GET_ITEMS_FOR_POS.

                       data: begin of ls_po,

                                ebeln type ebeln,

                                end of ls_po.

                                lt_po like table of ls_po.

                      loop at it_pos into data(lo_po).

                         ls_po-ebeln = lo_po->doc_id.

                         append ls_po to lt_po.

                      endloop.

                      select * from ekpo into ls_ekpo for all entries in lt_po where ebeln = lt_po-ebeln.

                           create object lo_item

                            exporting

                               ebeln = ls_ekpo-ebeln

                               ebelp = ls_ekpo-ebelp.

                           append lo_item to return.

                      endselect.

                  endmethod.

                  The control is inside the PO Item class, so there is no problem. Multiple tables, lazy loading, separation of concerns.

                  But as I started by saying, if ultimate performance is critical don’t go OO. For most programs that are not that performance critical, you gain maintainability, and ease of enhancement.

                  PS: Also I think it helps not to think a class will be reused everywhere, the class you are creating is for your package unless you are writing an API. This movement line item class is for a particular package, it’s not supposed to be used by everyone that shares the SAP instance (the mythical canonical class).

                  (0) 
                  1. Matthew Billingham

                    Exactly. I develop a lot in Java as well as ABAP. In both I’m writing applications – not libraries. If, while I’m doing either, I can see benefit in making it generally useful, I’ll do that. But there’s nothing wrong with a class that’s used in one place by one application.

                    Reuse is made easier by OO. But reuse isn’t the only reason to program in OO. Maintainability, stability, encapsulation, separation of concerns are also good reasons.

                    (0) 
                    1. Joao Sousa Post author

                      I believe it’s extremely useful to program in other languages besides ABAP, it gives you a new perspective.

                      One of the main problem (from my perception) is that people don’t get why packages exist and just create things like ZMM and ZSD.

                      (0) 
                      1. Matthew Billingham

                        I learned ABAP before I learned Java. But I learned Java before I did any ABAP Objects. I recommend that route… and a pragmatic approach. Pure OO doesn’t sit well with ABAP O.

                        (0) 
                2. Joao Sousa Post author

                  For the simple example, as “filter” already has a range for material numbers.


                  lt_materials = ZCL_MATERIAL=>GET_MATERIAL_WITH_FILTER( filter1 ).

                  ….build filter using lt_materials…..

                  data(lt_movements) = ZCL_MOVEMENT_ITEM=>GET_ITEMS_WITH_FILTER( filter2 ). 

                  or just create a:

                  data(lt_movements) = ZCL_MOVEMENT_ITEM=>GET_ITEMS_FOR_MATERIALS( lt_material ). 


                  since it is a common enough filter and it improves legibility. Two selects, the same as in procedural code.

                  (0) 
                  1. Shai Sinai

                    I’m afraid it’s not a good approach.

                    It might work for small business programs (which I call “access” programs), but it won’t work in real SAP high scale systems.

                    For example,

                    If you have 1 million materials and millions of movements, and would like to restrict the data according to both objects (i.e. Join) such approach will be very time consuming.

                    As far as I like good programming, if the user will have to wait another 5 seconds due to it, I’ll have to program it “ugly”.

                    (0) 
                3. Bruno Esperança

                  Shai Sinai,

                  BELIEVE ME, I know exactly what you are talking about.

                  IF and WHEN you need to process thousands of “things”, it’s hard to have a TRUE OO approach. For example, if you need to update thousands of sales order documents, it will be a PITA getting an object for each sales document, and updating each sales order doc separately. Huge impact on performance.

                  But, on the other hand, as the others are saying, OO is so much better for everything else.

                  So, basically, my humble opinion, just use (true) OO until performance demands otherwise.

                  Cheers,

                  Bruno

                  (0) 
                  1. Shai Sinai

                    Hi,

                    Don’t get me wrong, I’m one of the OO followers.

                    My point is that, according to my experience, it is only/mostly usable for single objects and not for mass data (selections). I would be more than happy to hear about a “magic” solution/paradigm that may handle also such cases.

                    You should better use classes anyway, of course, but it isn’t OO.

                    P.S.

                    I think that your example of sales order update should actually be implemented in OO since you cannot change multiple sales orders at once, but only one at a time anyway.

                    (0) 
                    1. Bruno Esperança

                      Maybe I should have said sales order items. In true OO every sales order item would be itself an object, correct?

                      And I so agree with you… programming with classes doesn’t mean you’re programming in true OO.

                      Cheers,

                      Bruno

                      (0) 
                        1. Bruno Esperança

                          I believe you know what I mean, Matthew…

                          But, giving you the benefit of the doubt, some people just replace routines with methods, and still develop very poorly.

                          For example, I’ve seen BAdI’s implemented with HUNDREDS of lines of coding in the “main” BAdI method. I’ve even seen INCLUDE’s in there!!

                          It’s in a method, but is it OO? No.

                          Cheers,

                          Bruno

                          (0) 
                          1. Matthew Billingham

                            It’s definitely the case that it’s possible to code in an non-oo way in an oo language.

                            I’ve really confused some consultants by putting additional methods in BADIs (which also doesn’t it make it OO). When all that’s in the BADI is an instantiation and a method call on that object, they really don’t know what is going on. 🙂

                            (0) 
                      1. Shai Sinai

                        Well,

                        that’s make a difference 😆

                        I still don’t see any reason why not programming the update model in OO.

                        You may still update the data in item object level, only save will be done in order object.

                        The selection of the items to be updated is another issue, however.

                        (0) 
    2. Matthew Billingham

      However, I must mention that your example may just prove the opposite since it demonstrates the main drawback of OO in SAP: SQL selections of mass data (which, in my opinion, cannot be handled efficiently in OO in current ABAP tools).

       

      This is simply not the case. I write OO all the time. I’ve written extractors in ECC for BW that extract large volumes of data – all in OO. I’ve written programs in ECC and BW that handles large datasets. I’ve even written (in objects) a generic class that buffers database selects in an easy to use manner – and was easily (because of its oo design) extended to read BW InfoObjects master data and from DSOs.

      Sure, the persistence framework is useless for large volumes, so it’s true that you can’t make database queries in an pure OO way – but so what? Using openSQL inside methods doesn’t violate any OO philosophy. They’re just program elements – as much as for in Java.

      (0) 
      1. Shai Sinai

        I agree that you can (and should) use classes even for large datasets, since it has many advantages of its own.

        However, it isn’t OO.

        (0) 
        1. Matthew Billingham

          That’s a matter of philosophy. Some people say that using internal tables isn’t true OO. I think that’s rubbish (with all due respect!). Using internal tables is as OO as using an int in Java. Similarly using openSQL is as OO as using for in Java. It’s part of the basics of the language.

          When I use persistence classes, I encapsulate them in business object classes with the usual CRUD methods. In some of those methods, I use openSQL. I don’t think that makes them one jot less OO then using query services. You think it does. It’s just a matter of opinion – but what matters is, it doesn’t affect the programming style, you don’t lose any of the advantages of alleged “true” OO, and it doesn’t affect performance. If it ain’t OO it sure as heck isn’t procedural!

          You might as well say that when I read a file in Java in a loop that that isn’t OO!

          Incidentally there is a design fault (feature) in the transport of persistent classes. When they’re transported the base, agent and persistent classes are just copied across. They’re not regenerated from the meta data. This meant, that when I developed software using query services in a higher release, and transported it to a lower release, I got syntax errors, because SAP had refactored object services. If I’d manually created the persistence class in the lower release identically to the higher release, it would have worked fine.

          (0) 
          1. Shai Sinai

            Well,

            actually I don’t think it’s a matter of philosophy, but I guess we’ll have to disagree on that (philosophically 😛 ).

            you don’t lose any of the advantages of alleged “true” OO

            Actually you do: You cannot represent the real business object, but have to create an hybrid object. One of the main drawbacks is you cannot reuse it (although you’ve already mentioned it’s not crucial to you).

            If it ain’t OO it sure as heck isn’t procedural!

            In my opinion, it is procedural programming, just being coded in the classes framework.

            In the same way, you can write a procedural program also in Java.

            (0) 
            1. Matthew Billingham

              The problem is that some people seem to say that “It isn’t possible to “truly” program in OO in ABAP, so I’m going to stick to procedural”.


              You might as well say that a Java class isn’t properly OO because the JVM pseudocode accesses the underlying assembly code in a non-oo manner.  In order to get true-oo, we’d have to chip architecture entirely oo, and code only in smalltalk.


              I think then we’ll never agree.

              My business object is entirely represented and accessed in an OO fashion. Under the bonnet – within the black box – there may function modules called that are part of a procedural framework. My object remains an object, entire and pure in its objectness.  I say it’s oo – you say it’s not.

              You say car-mee-na, I say car-my-na. You say bur-ay-na, I say bur-ah-na. Ah, let’s Carl the whole thing Orff.

              Carl Orff: Carmina Burana – YouTube

              (0) 
              1. Bruno Esperança

                Saying that ABAP OO isn’t true OO so it’s better to stick with procedural is just stupid.

                It’s like… there are only *some* benefits with ABAP OO, not *all* the benefits of true OO, so let me not take advantage of any??

                Funny…….

                I try to take as much advantage of the benefits of the OO we have in ABAP as I can, and when I mean “true” OO, I mean actually “objectifying” stuff, like a sales order being an object, a sales order item being another object… etc etc…

                Cheers,

                Bruno

                (0) 
          2. Volker Wegert

            That’s a matter of philosophy. Some people say that using internal tables isn’t true OO. I think that’s rubbish (with all due respect!). Using internal tables is as OO as using an int in Java.

            To take the analogy one step further: If internal tables aren’t true ABAP OO, then arrays in Java aren’t true OO either.

            (0) 
            1. Joao Sousa Post author

              OO is more about design that actual implementation. You can use classes and still develop in a procedural way if you don’t think and design OO.

              In my GET_ITEMS_FOR_POS example in the comments I use a structure, but the logic is object oriented and that’s what really matters. Would it make it more object oriented if the structure was defined as a class? No, it’s a just technical detail, and it would take more code and more time with zero gain.

              (0) 
          3. Joao Sousa Post author

            That’s a matter of philosophy. Some people say that using internal tables isn’t true OO. I think that’s rubbish (with all due respect!). Using internal tables is as OO as using an int in Java

            With all due respect… I disagree. Sure int isn’t a class, but it’s a simple number.

            A line in a internal table can have lots of fields, with logical dependencies between them. If a line in a table has quantity, net price and net value (which is quantity * net price) there is no logic in the line of the table to ensure consistency. There is nothing to ensure that a quantity field has a unit, or that the unit is valid. In Java the unit would be a class in itself, not a simple CHAR3.

            To conclude I don’t think you are making a fair comparison with “int”. Anyway I do use tables even in a mostly OO oriented program because you can’t do full OO in ABAP (because of FM, RFC, etc) and neither is it optimal. I agree with you, the rule is pragmatism instead of fundamentalism.

            PS: After reading the other comments, I get you were making the argument “by absurdity” 🙂 . Forget most of what I said. 🙂

            (0) 
  2. Jacques Nomssi

    Hello Joao,

    I have a problem with your use of getters in this case. As is, your logic depends on:

    1. Class ZCL_MOVEMENT_ITEM having a method GET_ITEMS_WITH_FILTER( )
    2. Object LO_MOVEMENT having a method GET_QUANTITY( )
    3. Object LO_MOVEMENT having a method GET_VALUE( ).

    I will argue this makes your logic resistant to change: you expose internal of the classes that are not needed for this operation.

    Since your requirement is to build the average of data encapsulated in objects LO_MOVEMENT, it feels IMO more natural to have the logic for this new operation also  reside in the class that knows more about the data. i.e. in class of object LO_MOVEMENT.

    Assume I create a new method GET_AVERAGE( ): I can move your logic into this new method and I am done. I pass a FILTER parameter and I receive an AVERAGE.

    With this, I only have to know about

       

    1. GET_AVERAGE( ) method of class / object LO_MOVEMENT.

    You could change the implementation within this method in many ways (mass select or buffering or access the attributes of the object directly or whatever…) and the rest of the code will not care.

    The point is that an object should hide its attributes and only expose code ABSTRACTION to the outside world to reap the benefit of a stable interface.

    regards,

    JNN

    (0) 
    1. Joao Sousa Post author

      Hello,

      Although you are somewhat correct, what you are seeing isn’t the whole program (and the code posted isn’t real). There are other classes, and the average price (unlike the quantity and value) is not a characteristic of the line item.

      In the real program:

      • I want to display the total value and total quantity in a ALV so I need them;
      • Although I simplified the context not to overburden the blog, the average price depends on more then just the movement line items. You have to take into account RE documents from MIRO posting and PR documents from revaluations. In that way, GET_AVERAGE can’t be a static method of the class Movement_item;
      • The code you are seeing is a method of another object that corresponds to the line of the ALV table and contains the GET_AVERAGE method;

      Regards


      (0) 
    2. Bruno Esperança

      Jacques,

      Why are you always such a negative guy? 🙂

      By the way, have you seen my File Reader? I’d like to hear your (negative) opinion on it 🙂

      Cdlt,

      Bruno 🙂

      (0) 
  3. Suhas Saha

    Hello Joao,

    I completely agree with your approach. We should try to “objectify” whatever “objects” we can lay our hands on. I follow this approach whenever i have a small volume of data to handle. For e.g., we had added a new element to the Business Partner screen and i had built the enhancement using Persistence class as the model. So far so good.

    But what about a mass processing application. For e.g., a batch program which changes the line items of the sales orders. Is it really advisable to build a line-item object? What is the “performance hit” we take while creating 1000x objects?

    Do you have any metrics you would like to share? I am not really good with the memory analysis stuff 😐

    BR,

    Suhas

    PS – One of my programs was rejected because i had built it exactly the way you have proposed. I was asked to refactor the whole application & i blatantly refused 🙂

    (0) 
    1. Joao Sousa Post author

      What is the “performance hit” we take while creating 1000x objects?

      If the constructor is efficient, the cost should be negligible, but I haven’t done any memory analysis to compare. It has been a long time since I have written a mass batch job, but for those situations, I never wrote one that was object oriented.

      One of my programs was rejected because i had built it exactly the way you have proposed.

      What was their reason to reject?

      (0) 

Leave a Reply