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.