Application Development 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: 
joao_sousa2
Active Contributor
6,381

As with most OO related discussion (i.eWhy object oriented? Import parameters), the arguments get interesting really quick. In that blog, a fellow SCN user described her dislike for OO, and how it relates to procedural, as the following:

The funny thing is that this aspect I dislike most about FM's is their greatest parallel to OO concepts. Basically you can say that a function group is something similar to an object, with the FM's being its methods. Fortunately, most of the time the FM's are independent and provide a well-defined functionality that only depends on the parameters you are providing at run-time.

The point the user is making is that class attributes are a lot like global variables, which are more annoying to debug since it's harder to find out where they were actually set. I really get where he is coming from. As always, it's a matter of design and following OOP guidelines.

What you should do:

  • Public atributes should be set in the constructor;
  • Private attributes should be set in specific getter/setter methods (or the constructor).

What you shouldn't do:

  • Change an attribute directly outside of the constructor and getter/setter methods.
  • Create attributes for temporary variables that are not related to the object itself;
  • Create attributes with obscure naming like WERKS, use Plant instead. Use _ if you have to, the attribute name should always be self descriptive.

The rule of thumb is: If the attribute is not worthy of public or a getter/setter then it is temporary method variable and shouldn't be an attribute at all.


This explains why they aren't so bad if used well, but doesn't explain why you should use them. I think the most immediate way to summarize their power (in the context of SAP WM, and TO confirmation) is this:


lo_to->confirm( ).


Why is this powerful? Because import parameters are messy, and the more "the merrier" (joking). Translation, the more parameters, the more mistakes you can make and the more information you need to use them correctly.

This is the exact opposite of the FM with zero state, where all information must be provided by IMPORTING parameters. Lets picture the scenario where you need to confirm a TO (from SAP WM) without using OO attributes. SAP provides a FM for that:



    call function 'L_TO_CONFIRM'
      exporting
        i_lgnum                        = lv_lgnum
        i_tanum                        = lv_tanum
        i_squit                           = lv_squit
        i_komim                        = lv_komim
        i_tbeli                            = lv_tbeli
        i_update_task                  = 'X'
      tables
        t_ltap_conf                    = lt_ltap_conf[]
        t_ltap_conf_hu              = lt_ltap_hu[]




This is not an extreme example there are way more complicated FM interfaces. There are two main problems with this:

  • Filling lt_ltap_conf and lt_ltap_hu is far from trivial;
  • The FM call is completely coupled to the calling program (more on that in an instant).

I've used this function in RF programs which are a pain (if you ever touched one, you will know it...), and have persistency requirements: a warehouse worker can't lose his work if Wi-Fi goes down, so every item picked gets saved to the database. Now let's assume that an error is thrown at TO confirmation, and you need to find out why.

If you used the traditional procedural way, you would need to run the calling RF program, get to the place where you make the TO confirmation and debug (with the extra annoyance that RF is system-guided, so getting that specific TO means superuser specific code). Either that, or you could SE37 the FM and fill by hand all the parameters of the FM (remember the counterexample is a FM with not state).

With OO, it's as simple as SE24, create instance, insert number of TO (and warehouse number), and then press "CONFIRM". Two parameters and you are ready to debug. I cannot stress how important this decoupling becomes when you are debugging errors or making unit tests, this separation between caller and callee means you can focus on particular behaviors.

This is a specific situation with persistence where OO gives you a really advantage. I've found over the years that dealing with persistence is one of the greater advantages of OO since most of the data is already available. In a more general situation the CONFIRM call would be preceded by calls to ADD_ITEM, but even that is simpler than manually filling the lt_ltap_conf with absolutely no rules (check Why object oriented? Import parameters for my argumentation on that subject).

All this could be made using Function Groups, since this blog more about Object Oriented Programming then Classes/Methods.

75 Comments
UweFetzer_se38
Active Contributor
0 Kudos

No worries, I'm not entering the discussion on the main toppic again, I just want to clarify the "fancy Eclipse tools":

Eclipse is the way to go for ABAP developers. SAP is not enhancing SE80 anymore. For example the CDS-Editor exists only in Eclipse and will not be "downported" to SE80.

See New Data Modeling Features in SAP NW ABAP 7.4 SP5

Private_Member_7726
Active Contributor
0 Kudos

Hi,


Jens Petersen wrote:



I prefer a classes for ease of seeing what is private and and what is public.



I take it that you are talking about private and public attributes.



More so - the "functions" or the methods...


The reason is that private attributes modify the object behavior without the user of the instance having any chance to predict it. IMHO an object should have a defined behavior, which depends on the parameters you pass to it and its state as described by its public attributes.



They may modify behavior in an unpredictable fashion, but that's a programming error then... You can't seriously be arguing that by restricting your as user's access to some data in itself I must necessarily have made something unpredictable or reduced the understandability of my public interface...


Firstly, I prefer to control instantiation - via "privatizing it" and providing appropriate static CREATE_...( ) methods. I also try to keep the "state" of object as small and manageable as possible and to make even the private "state changes" as explicit as possible - over appropriate private SET_ method only (yes, more "overhead" still, and departing from SAP's programming guidelines in that tiny-miny accessors get written). Even for setting initial state in constructor(s) (apart from instatiating private member objects obviously)... That way I have easy way of enabling the state changes for public use, should the need arrise. And private state checks often (and for flags always) happen over an explicit IS_...() method.


Well, you need to define a variable of TYPE REF TO your class, then fill it by creating an instance, then using it to access the method. That is obviously an overhead compared to simply calling an FM by its name.



Ok, the coding overhead... Yes, I agree, but even the brevity concerns should have been fully adressed by NEW constructor expressions/intantiation operators as well as the changes to (chained) functional method calls in 7.4 , I believe (more hope than believe, actually, because I have not had the pleasure to work on 7.4 yet :sad: ).

cheers

Janis

joao_sousa2
Active Contributor
0 Kudos

Tough luck there, 7.4 is awesome. All the ABAP changes, and integrated Gateway.... It's a pain to go back to older systems.

And when he says that there is extra overhead he is comparing a stateless FM, to a stateful object, and still it's less verbose the OO way.

data(lo_delivery) = new ZDELIVERY( lv_vbeln ).

lv_items = lo_delivery->get_number_itens( ).

Versus

CALL FUNCTION 'GET_NUMBER_ITEMS_FOR_DELIVERY'

     EXPORTING

          I_VBELN = lv_vbeln

     IMPORTING

          E_number = lv_items.

PS: And while the former has syntax and type compatibility checks, the latter may get you a nice dump (wrong type, or FM doesn't exist).

SuhaSaha
Advisor
Advisor
0 Kudos

Let me spew a lil' bit more venom, if i may, :wink:

  1. Inline data-declaration is not allowed for FMs (meh)
  2. Parameter name is not checked until runtime (meh meh)
jensupetersen
Participant
0 Kudos

Eclipse tools aren't "fancy" they are the abap programming tools going forward.


Well, basically Eclipse is a language-independent tool that has nothing to do with ABAP except that SAP supports using it with ABAP. I sincerely hope that SAP will keep providing the tools required to develop in their software in a modern fashion, meaning that you should not have to rely on such tools.


I hate JavaScript and yet I'm worried that I'm not doing enough SAPUI5. Sticking to what we like or are confortable with is a great way to become obsolete.


Unfortunately you are right. What does it take to develop in SAPUI5? Isn't that some sort of cloud technology? Will it work with a a conventional 7.31 server?


They may modify behavior in an unpredictable fashion, but that's a programming error then...


Well, if the private attributes never have any impact on the behavior or result of a method, then what is their purpose? As I said, I can imagine method-internal pre-buffering data for performance purposes, but hardly anything else. If it has a meaning, it has an impact. If it has no impact, there is no reason for it to be there in the first place. In that case, you could just replace it with an internal DATA command that starts fresh at every method call.

joao_sousa2
Active Contributor
0 Kudos
Well, basically Eclipse is a language-independent tool that has nothing to do with ABAP except that SAP supports using it with ABAP.

It is the chosen tool going forward for ABAP, and all new features will be added to it (not SE80). The part about having to rely on it, well it depends on your frame of mind, if you don't want new Netweaver features... sure, SE80 won't disappear.

Unfortunately you are right. What does it take to develop in SAPUI5? Isn't that some sort of cloud technology?

It is a HTML5 based framework. Fiori apps are based on it, it is the SAP technology used to develop responsive applications. You can use Eclipse or AppBuilder (AppBuilder Tutorial - Tab Based App)

Well, if the private attributes never have any impact on the behavior or result of a method, then what is their purpose?

He didn't say that. Anyway, you talk about buffering so you do acknowledge the advantages of state, in a critical area like performance. Most of the times I see SAP use global variables it's because of buffering.

And most of the time my greatest problem is discovering where that buffered data was filled, which is a problem solved by using class setter/getters like I described in the blog.

0 Kudos

Hello Joao,

Nice article :smile:

It is very useful for me :smile:

I learnt a lot from all conversations :smile: Thank you guys :smile:

Nice evening

Gabriel

Former Member
0 Kudos

Jens,
I just wanted to add one point:

It seems that you are consistently confusing between (theoretical) class maintenance/debugging difficulties and the ease of use of (existing) class.

These are two different aspects...

jensupetersen
Participant
0 Kudos
Well, if the private attributes never have any impact on the behavior or result of a method, then what is their purpose?

He didn't say that.


I never claimed he did. But if he actually did not agree to it, then he would have to be of the contrary opinion: that there are some legitimate private attributes that do have an impact on the behavior or result of a method. That, however, would have the negative implications I pointed out before and pretty much defeat the core goals of OO, particularly the goal of having a well-defined interface for a clean encapsulation (seeing that these private attributes would effectively act as hidden parameters that influence the result of the method call without being part of the method signature). The outcome would be even worse than if you were using global variables: The content of global variables may have a blurry origin, but at least it can be accessed at any time in the program. Having obscure variables influencing your program that your code cannot access at all is even worse, IMHO.

Knowing that the following is purely academical: If I was in charge, I would remove both private and static attributes altogether (seeing that the latter are effectively global variables of classes, thus being an invitation for sloppy coding and no better than any other global variables). For buffering purposes, I would allow all flavors of the STATICS command in methods. That way methods still would have the chance to do some buffering for performance reasons, but language-wise it would be clear that this is only a part of the implementation and not a feature of the method. Variables for buffering with no relation to method outcome and behavior should not sail under the "method attribute" banner.

Private_Member_7726
Active Contributor
0 Kudos

Hi,

When you have put it that way, I do see your "philosophical"  and practical point and actually wholehartedly agree with part of what you are saying - the state changes affecting behaviour should be made transparent to the user. If I remember correctly, SAP suggests the use of public read only attributes. I disagree in part and prefer to try to reduce state queries to explicit IS_THIS_TRUEORFALSE( ) methods (public read-only member objects is another matter). That way I have hidden my implementation details of coming to true or false (and thereby reduced the coupling somewhat), and have the flexibility to change/extend them so long as I have not affected my public contract.

But lets make a clear distinction - querying the state and changing the state are two entirely different matters that need a nuanced, case by case approach. Yes, there are obvious cases of changing the state that must be publicly available - buffers must be resettable (again, obviously, using the methods and not directly on class attributes) or I as user must be able to disable the buffers (prevent object from entering certain undesirable states) entirely (I know how to implement buffering for my application, if your implementation does not fit my needs or breaks me). I as a user must IMO be able to control the SAP locks an object may set, etc. But it's a matter of API design, being as flexible as possible and coming to an agreement... all these matters do not IMO necessitate utter rejection of parts of OO concepts and way of working.

cheers

Janis

joao_sousa2
Active Contributor
0 Kudos
That, however, would have the negative implications I pointed out before and pretty much defeat the core goals of OO, particularly the goal of having a well-defined interface for a clean encapsulation

That's not encapsulation, in fact encapsulation is almost the exact opposite. Encapsulation (object-oriented programming) - Wikipedia, the free encyclopedia

If I was in charge, I would remove both private and static attributes altogether (seeing that the latter are effectively global variables of classes, thus being an invitation for sloppy coding and no better than any other global variables).

You are basically against stateful programs. For you the number one rule that must not be broken is everything should only depend on inputs, which is basically stateless only. If you can't understand the advantages of state in a program (which I actually showed in this blog), I won't be the one to convert you.

jensupetersen
Participant
0 Kudos

Well, I do not see what is wrong with public read-only attributes. I may be lacking OO practice, but I even tend to the opinion that all object attributes should be read-only for the outside world and changed only by methods of the object. However, I do not see why that should be an incentive to make those attributes private. The traits "public/private" and "full access/read only" are independent from each other and should not be confused.

However, while changing an attribute should require a method, I fail to understand why you postulate that reading one should require a method as well. IMHO the IS_THIS_TRUEORFALSE( ) method you suggested has no advantage over a simple public read-only attribute of boolean type. But your method will make the object harder to understand and to wield, because its current state is not completely visible by its public attributes at all times, seeing that vital information is hidden inside for no apparent reason. Simply having the boolean public attribute makes the feature obvious, saves you the hassle of coding the method, and saves the user of the object the hassle of calling the method (doing a simple IF on the public attribute of your object will make his code easier to read as well).


or I as user must be able to disable the buffers


That depends on circumstances. There are many cases in which there is not really a risk that a buffer content might become obsolete during the lifetime of an object instance. I agree the opposite can also happen. If that is a realistic case, offering a FLUSH_BUFFER method is indeed called for.

joao_sousa2
Active Contributor
0 Kudos
However, while changing an attribute should require a method, I fail to understand why you postulate that reading one should require a method as well.

Encapsulation (the true meaning). Hiding the internal implementation from the user of the class/method. I already gave an example, lazy loading. If you manipulated the attribute directly you couldn't make sure that the attribute was loaded when you manipulated it.

Private_Member_7726
Active Contributor
0 Kudos

Jens Petersen wrote:



However, while changing an attribute should require a method, I fail to understand why you postulate that reading one should require a method as well. IMHO the IS_THIS_TRUEORFALSE( ) method you suggested has no advantage over a simple public read-only attribute of boolean type.




So I retain the freedom to change the implementation - the way I come to the true or false value. Ranging from simple renaming of the attribute to refactoring this part of state out of the class entirely and delegating keeping it to another class, for example - without breaking/having to adjust the existing uses (and obviosly with the express intent not to change the existing behavior).

Edit in: Perhaps it's more illustrative if it's not simple boolean being encapsulated (I hope I'm using the term correctly here) - is_cash_payer( ), where the conditions can be much more complex and might need to be extended many times over, say, 10 years... Or changing from boolean to, say, 4 fixed value attribute...

cheers

Janis

jensupetersen
Participant
0 Kudos

That's not encapsulation, in fact encapsulation is almost the exact opposite.


You fail to realize that I was not defining encapsulation, so yes, that "was" not encapsulation. I was making a statement about how private attributes undermine the goal that is being pursued by encapsulation.


You are basically against stateful programs.


Only as long as the state is hidden from the outside world, so when you call a method, you can only pray its state is such that you will get the desired result. There is nothing wrong with meaningful program states when they are readable from the outside world in the form of public read-only attributes (and changeable by suitable methods where needed).


If you can't understand the advantages of state in a program (which I actually showed in this blog)


TBH the example you gave was pretty poor. You compared an existing FM with a usable, but complicated interface to an OO method with an apparently easier interface (again, I am not at home in WM, and you gave so little information about that method that it is hard to tell).


One thing is sure:


lo_to->confirm( ).


will not do anything with a given TO unless you specify said TO somewhere earlier. Seeing that you are talking about states, I assume that you "filled" it into the object in an earlier command which you conveniently neglected to mention. Now omitting half of the necessary commands is an easy way to prove how "short" OO coding allegedly is...

Besides this formal mistake you made, your main point, being that this is way easier and more powerful than doing it with an FM, is not proven either. You bent circumstances towards where they serve you best by comparing an FM with complicated interface to a method (or set of methods, seeing that you need to "fill" your object up front) with probably an easier interface (that you neglected to describe). Based on your SE24-related reasoning, I take it that you just need to fill the TO number into your initialization method (probably into the constructor), and the method will then look up all the related data on its own. This can obviously done with an FM just the same, only that the one you named is older, thus requiring more detailed input. However, it would be no problem to create another FM with only the number of the TO as input parameter and have this FM look up all the required data of the TO and then call the FM L_TO_CONFIRM with it. SAP is doing this thing with nested FMs all the time.

Bottom line is that this is not a question of OO vs procedural; it is merely a question of "powerful routine" vs "less powerful routine". I could as well code a powerful FM and a method that requires lots of input and then try to use that as a counterproof (which would be just as invalid).

Besides, FMs support stateful programming just the same, just not multiple instances of it. You could always create a function group with one FM to "load" the TO into the globals of the function group (= equivalent of private object attributes) and a parameter-less FM to confirm it. Then I could write a CONFIRM-call which is just as short and amazing as your lo_to->confirm( ). Essentially you are still passing parameters, only that you do it in a prior command.

The only possible advantage of OO here is the potential of supporting multiple TOs at once by means of multiple object instances. But that does not appear like a probable scenario in real programs, seeing that SAP states that you should not run internal tables with multiple object references for your documents, but instead store the relevant document data right in the columns of your table (official SAP recommendation as Jänis B pointed out before). So seeing that you are not going to have multiple TO objects in memory at once, there is no advantage in doing this OO (aside from the fact that SAP does provide the more powerful functionality in OO form than in their older FM, but that is a matter of existing code, not of programming technique).

jensupetersen
Participant
0 Kudos

Ok you provided a valid point (although a simple renaming of the attribute is not really a valid case because you could as well want to rename the method for some reason, forcing you to modify the method signature just the same. However, for both attribute and method it can be asked why such a wanton renaming should ever be necessary. I suggest we stick to your other examples, which I consider perfectly valid).

joao_sousa2
Active Contributor
0 Kudos
will not do anything with a given TO unless you specify said TO somewhere earlier. Seeing that you are talking about states, I assume that you "filled" it into the object in an earlier command which you conveniently neglected to mention.

You've made several ad hominem implications before, with this one I'm out of the discussion. Learn to argue without resorting to these sorts of jabs.

jensupetersen
Participant
0 Kudos

You have stated broad affirmations like


For you the number one rule that must not be broken


and now whine about the simple word "conveniently"?


I'm out of the discussion.


Farewell.

fred_verheul
Active Contributor
0 Kudos

Hi,

I'm posting this as a new comment to have more width on the page, but it's really meant as a reply to this comment from joao.sousa2 and this comment from jensupetersen (I'm sure you guys can guess which).

Please let's keep the discussion civil, and not get emotionally involved. At least not too much. Many good things have been said, and there's value in this whole discussion. I have a feeling though that we are at a saturation point, and that going on might have diminished returns.

Specifically for you Jens: you can't go on forever like this without starting to irritate people. Several valid arguments have been presented in favor of OO. Meanwhile you still don't see the advantages. That's possible and even okay of course, but I want to suggest that this might be a good moment to stop the discussion here on this blog post, and try to educate yourself on OO a little more. Even without direct relevance to your day job (in which you might indeed not see the need for much OO programming, I'll give you that), it can still make you a better programmer if you learn more about it, practice more with it (not necessarily in ABAP), and get a feel for how things work.

Afterwards, by all means come back to SCN, and either continue the discussion here, or write your own blog post sharing how and why it changed (or didn't change) the way you look at programming in ABAP.

For reference (and fun!) I also want to point you to the blog posts by paul.hardy, who's also been trying to get his head around OO and discovering the benefits of ABAP-OO (if there are any). Good stuff!

But again: let's at least keep the discussion pleasant.

Thanks guys!

Former Member
0 Kudos

This is something I like doing in my code but unfortunately not every developer likes because of the additional effort.

IMHO, not following this approach means having the same headache as with global variables in routines/FM where you have to pay attention where to put your code because you don't know if the variable is already initialized or not.

wim_verschueren
Explorer
0 Kudos

The goal is to adhere to the principle of data encapsulation as much as possible, so indeed avoiding public attributes.

But there are some exceptions in which I allow myself to define public attributes:

* defining constants which are useful outside the class too

* defining some key-attributes as public READ-ONLY:  this allows you to find easily back objects based on specific identification-attributes.  For example: you could use "READ TABLE lt_my_object ... WITH KEY table_line-><my_public_read_only_attribute>."  This is especially for performance reasons (in some cases it is a dramatic improvement), otherwise you need to loop over the table and call for each object the GET-method to identify if that is the object you are looking for.

Former Member
0 Kudos

Lucas, you're bang on with what you are saying about encapsulation. Thank you for making that point.

I would like to ask your opinion on public READ-ONLY variables. My mentor and I were discussing the use of these. I advocate for using getter and setter and not making direct references to the read only variable. I believe directly referencing these, breaks encapsulation, breaks consistency and can hurt modularity. He believes that if it is made public read-only only consistency and potentially modularity are violated. Your thoughts?

jensupetersen
Participant
0 Kudos

I am not Lucas, but I believe making such a distinction and introducing additional overhead by creating such single-line methods is petty. A public attribute that deserves the name :!: is one whose semantics is clearly defined and should never change. As such it should not be direct part of the internal computations of the method (which may be subject to future changes). In doubt you can always have a private variable holding your results which you then copy into the public attribute. By doing so, you consider the latter as what it is: part of the interface.

Often enough, not even that should be necessary. If the purpose of your method fetches, say, the name of a material number, then that material number itself may well be a public attribute, seeing that it belongs to the very basis of the functionality. A change of logics concerning the material number in such a method would defeat the whole method; it would cause the method to serve a totally different purpose, effectively being a different method. However, changing a method into a totally different method with a different purpose obviously does not allow for stability in any respect.

Unless, of course, you only keep those interface variables for downward compatibility and fill them with meaningless default values (you cannot fill them with the expected values if your method has completely changed its purpose, thus stopping to provide those). But under those circumstances I consider it infinitely better to leave the method as it is, stopping to use it if its purpose no longer fits into your concepts, and making a new method for your new needs rather than abusing the old one for that purpose.

joao_sousa2
Active Contributor
0 Kudos

A public attribute that deserves the name :!: is one whose semantics is clearly defined and should never change.


Until they do. I use public read-only for key values like material number which is intrinsic to the object and a key.

As a rule public read-only must be handled in the construtor, because otherwise you have no way of making sure the access is correct. If you put too much in the construtor, things you may never use, it will weight on the performance.

jensupetersen
Participant
0 Kudos

Until they do.



If they do, you messed up in the first place, IMHO. Any such attribute should be well-defined enough never to "accidentally" change (except in case of a bug, which is outside our scope here). If you need a different value to be exported, leave the attribute as it is and introduce an additional one. If the change is even bigger than that, make a new class.


As a rule public read-only must be handled in the construtor, because otherwise you have no way of making sure the access is correct.



Not necessarily, at least not in the sense that it requires any action in the constructor. For instance, if your class is "parking ground" and you have a public integer attribute "# of occupied parking slots", then upon creating a new instance the number will default to 0, which is correct.


If you put too much in the construtor, things you may never use, it will weight on the performance.



True if obtaining the information for those attributes requires serious computations. For those, methods are the approach of choice anyway, but I do no think you would label such a method a "Get" method.

For public attributes, I am thinking more of trivial (yet necessary) information, which is easy to calculate, like the # of available parking slots. A single initialization line in the constructor like

total_parking_slots = 25

is so negligible performance-wise that it will hardly ever matter. The effort for creating an instance is far higher than such an assignment, so if you have a performance problem, it originates from elsewhere.

Labels in this area