Skip to Content
Personal Insights
Author's profile photo Kamil Kovac

PUBLIC READ-ONLY: Best invention since sliced bread, or unspeakable travesty?

Declaring Class Attributes as PUBLIC READ-ONLY can be quite polarising. To some, it is bad practice to be avoided at all costs. To others, it’s a life-saver.

I can see the arguments for both sides, but cannot decide for myself.

Why use:

  • It saves you the work of declaring code-littering getters.
  • It enables accessing Objects in an internal table by their PUBLIC READ-ONLY Attributes, such as:
LOOP AT lt_objects WHERE mv_my_attribute
SORT lt_objects BY mv_my_attribute

This behaviour can be leveraged in a number of design patterns, e.g. in a Decorator.

 

Why avoid:

  • Having a dedicated getter is actually a Good Thing. Especially if you enforce accessing Class Attributes via their setters and getters even internally, within the Class itself. Then you can put a BREAK-POINT inside the getter / setter, and be sure it’ll stop every time anyone tries to access the Attribute. This is easier than chasing the Attribute down its where-used list.
  • This syntax doesn’t exist in other programming languages.
  • Eclipse generates the getters and setters for you automatically, so what’s the big deal anyway?

 

I have worked in an environment where PUBLIC READ-ONLY was considered bad practice, and never used. It was required that Class Attributes be accessed by their getters and setters always, even from within the Class.

No

mv_my_attribute = ABAP_TRUE

but rather

SET_MY_ATTRIBUTE( ABAP_TRUE )

 

No

IF mv_my_attribute = ABAP_TRUE

but rather

IF GET_MY_ATTRIBUTE( ) = ABAP_TRUE

 

The Architect was successful at enforcing this rule in code reviews. However, it cost him time and energy, especially when a new member of the team joined.

I would be keen to hear your opinions on the topic. Is PUBLIC READ-ONLY to be encouraged, or discouraged?

 

Assigned tags

      19 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Vicenç Xavier Lozano
      Vicenç Xavier Lozano

      My main concern is why do you think sliced bread is a good thing?

      Didn't know about public read-only things... sounds nice, comfy and fast, so I will try to forget it ASAP. In my experience, if something in ABAP is nice, comfy and fast, it's usually because it's a thing to avoid.

      But kudos to you! I will think deeply about the getters/setters thing even internally. Sounds hard, so may be something good.

      Author's profile photo Kamil Kovac
      Kamil Kovac
      Blog Post Author

      Thank you, Vincenç. Your philosophy "If it's nice and comfy, it's probably bad - and vice versa" will serve you well 🙂

      Author's profile photo Michael Koval
      Michael Koval

      I like to use PUBLIC READ-ONLY attributes for keys in business objects (in the general sense, not SAP Business Objects). This is necessary sometimes since character-type database keys are the most common way to interface with the rest of the ERP-framework.

      However this only works if object creation is aborted in the constructor when the constructor parameters are malformed, which is a controversial practice itself.

      Author's profile photo Kamil Kovac
      Kamil Kovac
      Blog Post Author

      Thank you, Michael.

      I'm not sure I follow...

      (That's a roundabout way of saying that I didn't understand 🙂 )

      Would you care to elaborate, maybe with an example?

      Author's profile photo Michael Koval
      Michael Koval

      Sure Kamil, here's an example:

      class invoice definition create public.
        public section.
          types: begin of ty_key,
                   belnr type re_belnr,
                   gjahr type gjahr,
                 end of ty_key.
          methods constructor
            importing
              header type rbkp.
          methods post
            raising
              cx_static_check.
          methods delete
            raising
              cx_static_check.
          "...
          data key type ty_key.
      endclass.
      
      
      class invoice implementation.
        method constructor.
          check_existance( header ). "raises exception if invoice is not in db, for example
          me->key = corresponding #( header ).
        endmethod.
      "...
      endclass.
      
      
      "later...
      data(some_invoice) = new invoice( header ).
      select count ( * )
        from rbco
        where belnr = some_invoice->key-belnr
        and   gjahr = some_invoice->key-gjahr.

      The method before setting the key makes sure you do not instantiate an object which does not have a corresponding entry in some database table. The caller will never get an initial key, because the object creation would fail in that case. Like I mentioned earlier, raising exceptions in the constructor is usually a bad idea. However, I think in this case it's justified.

      In the event that you do work with a SAP Business Object:

      data(some_invoice) = new invoice( header ).
      cl_swf_evt_event=>raise( im_objcateg = cl_swf_evt_event=>mc_objcateg_cl
                               im_objtype = 'ZINVOICE'
                               im_event = 'SOME_BUSINESS_EVENT'
                               im_objkey = some_invoice->key ).

      I hope that helps.

      Author's profile photo Kamil Kovac
      Kamil Kovac
      Blog Post Author

      Thank you for taking the time, Michael. Now I see what you meant. An example is worth a thousand words, once again.

      Author's profile photo Manfred Klein
      Manfred Klein

      Erm, sorry for the stupid question: But don't you normally first check the existence of a ressource before creating an object for storage?

      Imagine being that object coming to life(constructor): 'Hey, nice to be alive! What am I supposed to do? Ok, storing content/reference to some ressource. Oh, the ressource isn't there. Reading manual what to do in this case. Commiting suicide?? Are you serious? *BANG*'

      Sounds a little cruel ;-).

      Author's profile photo Michael Koval
      Michael Koval

      Not all objects represent existing resources. If the object you want to create does and the resource is unavailable, you have two options:

      1. raising exceptions in the constructor, aborting the subsequent program
      2. raising exceptions on first method call, allowing for error free composition but failing in execution

      An example for option 2:

      data(gui_invoice) = new displayable_invoice( new invoice( header ) ). "invoice is deleted
      gui_invoice->display( ). "displays message about invoice being deleted instead of short dump

      A call inside of displayable_invoice to invocie would catch an exception and implement suitable error handling for the situation, e.g. informing the user that the invoice is deleted and cannot be displayed. If the constructor fails, we would get an exception on line one, forcing us to surround the two lines with a try-catch-block.

      It is possible and sometimes preferred to create objects even if the underlying resource is unavailable, for the sake of object composition.

      Author's profile photo Georgi Slavov
      Georgi Slavov

      I myself prefer getter/setters mainly because in OO you are supposed to be telling a story, and the 'sentences' so to say consist of 'verbs'. So somehow, more pleasing to the eye.

      But at the end i guess it depends mostly on your overall design, question being if you are sure that in the context of the object/and related/inheriting objects the operation is and always will be a pure 'get' ( = ) . If you doubt that in future there could be added also some calculations, i would prefer having a method.

      And yes, you save time with the direct access... Have seen SAP developments where this was stated as a significant advantage. Actually it was always an ABAP event -driven architecture producing a huge number of redundant method calls...

      Author's profile photo Kamil Kovac
      Kamil Kovac
      Blog Post Author

      Thank you, Georgi. Good point about the code telling a story. Robert C. Martin used to say that, too - about code being ideally written as a newspaper - headlines first as Method names, you can dig into the story by looking inside the Methods.

      Regarding your second point - what happens if a getter evolves into something more that a simple one line of code passing a Parameter value outside. Should it? You could argue that as soon as a getter needs e.g. some calculation, it should be renamed to something else. Keeping the original getter a "dumb" one line of code. Alas, I've seen both kinds of getters. Including some with fairly complex logic.

      Author's profile photo Jan-Willem Kaagman
      Jan-Willem Kaagman

      I would like to encourage using getters and setters. Most importantly because it encourages thinking about encapsulation. Using direct access to (modifiable) attributes makes you forget about encapsulation, and while it might not have a great impact on a public read-only, it forces you to think about using object-oriented paradigms more often.

      Having said that, I need to rethink the use of constants defined as public static constants. Should I use a getter for that as well?

      Author's profile photo Michael Koval
      Michael Koval

      Hi Jan-Willem,

      getters and setters are the antithesis of encapsulation and object thinking. I was surprised and agitated when I learned this myself. This blog has some thought provoking arguments on the matter and a highly entertaining comment section: https://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html

      Author's profile photo Jan-Willem Kaagman
      Jan-Willem Kaagman

      Nice article, thanks for sharing. Although I am still not finished with all the comments, I agree that getters and setters are evil. But if you have the choice between opening up attributes of a class without any encapsulation compared to a public read-only attribute, I prefer getters and setters to direct access.
      I think the same argument in the article for getters/setters can be applied to direct access to attributes. To stay in the spirit of the article:

      Data(ball) = dog->ball. 

      sounds even worse to me than

      Data(ball) = dog->get_ball( ). 

      Although, after reading the article, both are evil.

      Author's profile photo Michael Koval
      Michael Koval

      Both styles reinforce the notion that the object has data that the caller can access at any time, hence making the object "dumb". A preferred way of writing would be:

      data(ball) = dog->ball( ).

      It's a request to the dog to pass the ball. The dog might deny the request and give you an exception instead (which is not possible with PUBLIC READ-ONLY attributes).

      This idea is called "Builders and Manipulators" and further elaborated here: https://www.yegor256.com/2018/08/22/builders-and-manipulators.html

      Author's profile photo Kamil Kovac
      Kamil Kovac
      Blog Post Author

      Lately I've come across a number of articles like this on Medium. How the OOP as it is practised today leads to blobs of "promiscuously shared mutable objects". How this is contrary to the original idea behind OOP, which was that objects should exchange information among themselves, much like cells in a living organism. Instead of SETting and GETting states.

      Author's profile photo Kamil Kovac
      Kamil Kovac
      Blog Post Author

      Thank you, Jan-Willem. Getters for public constants would be an overkill, in my opinion.

      The Clean ABAP initiative recommend using Enumerator Classes for public constants. In the first example (Constant Pattern) you can see that they are referring to the constant directly.

      BTW, in the second example (Object Pattern) they are using PUBLIC READ-ONLY attributes.

      Author's profile photo Laurens Deprost
      Laurens Deprost

      Thanks for the interesting read!
      I'd always prefer setters and getters versus public read only attributes for the reasons the post mentions.

      That said, the use of setters and getters in itself is certainly an interesting topic for debate.
      The blogs in the comments above are quite interesting indeed.

      Author's profile photo Matthew Billingham
      Matthew Billingham

      The Clean ABAP book says "Consider making immutable attributes read only". Why? "Public read-only attributes are easier to consume and more readable than the corresponding accessor method".

      You might not want to do this if you want to return an abstraction to the value.

      The following advice is also given if you want to use READ-ONLY.

      1. Don't change the value of the attribute within the class. It should be set only at instantiation
      2. Set the class to FINAL - so no inheritance
      3. Don't have any friends to the class.

      In this way, no one except you, the developer, can fiddle with the value of the attribute. A use case is the class based enumeration classes mentioned by Kamil Kovac 

      I really can't see much point in insisting on only using getters and setters to access attributes within their own class.

      Author's profile photo Kamil Kovac
      Kamil Kovac
      Blog Post Author

      Thank you for your answer, Matthew, and for pointing me to the Clean ABAP Book.