Chris Hermansen

7191 points
Chris Hermansen portrait Temuco Chile
Vancouver, Canada

Seldom without a computer of some sort since graduating from the University of British Columbia in 1978, I have been a full-time Linux user since 2005, a full-time Solaris and SunOS user from 1986 through 2005, and UNIX System V user before that.

On the technical side of things, I have spent a great deal of my career as a consultant, doing data analysis and visualization; especially spatial data analysis. I have a substantial amount of related programming experience, using C, awk, Java, Python, PostgreSQL, PostGIS and lately Groovy. I'm looking at Julia with great interest. I have also built a few desktop and web-based applications, primarily in Java and lately in Grails with lots of JavaScript on the front end and PostgreSQL as my database of choice.

Aside from that, I spend a considerable amount of time writing proposals, technical reports and - of course - stuff on https://www.opensource.com.

Authored Content

Authored Comments

Thanks for your comments, Bernd. It's always great to engage with readers, really appreciated.

Let's first review what we're trying to accomplish - we go to a bulk products store and buy a bunch of bulk packages. We take them back to the office. We take the bulk packages apart and redistribute the units into hampers of approximately equal value, and we give those to the neighbours.

Let's look at Golub's principle here "get the object to do work" and how my classes fit or don't.

The Pack class has a method called unpack(). This precisely follows Golub's principle - it's how I tell Pack to give me a list of its constituent Unit instances.

Similarly Bought has an unpack() method, that gives me a list of the all the Unit instances in the number of Pack instances purchased. So again following Golub's rule.

At this point, we could note that we have actually bought some self-unpacking bulk materials, set them on the floor of the office, and told them to unpack themselves; we end up with a list of the constituent parts, called Units.

Let's look at the other rules mentioned in your link.

"violated encapsulation principle" - actually the point of getters and setters is to help with encapsulation. Things outside the objects cannot reach inside them; they have to communicate by known pathways. Over and above that, my classes are effectively immutable as I mentioned - no setters - so there is no possibility of injecting anything; the field values are established when Unit, Pack and Bought instances are created and can't be subsequently altered. Moreover the field values cannot be viewed from the outside because the fields are private. So I can change the internal structure any way I want, as long as I can continue to honour the contract established by the getter methods. The Unit itself is a tuple, existing only to encapsulate the key characteristics of the unit concept - what the unit is, who made it and what is its value. We NEED to know these things because we're going to put those units into hampers and the people receiving the hampers need to be able to see what's in them and plan how to use them.

"exposed implementation details" - again, the point of getters and setters is to avoid exposing implementation details. The classic example is the concept of "geometry", where the user needs to know some details about the encapsulated geometry - for example, its length or area - without knowing how it is stored. To that are added the Golub-style needs, like "compute the polygon of intersection of this geometry with another geometry".

The other point made in the article you mentioned is "objects are / are not simple data holders". I agree that objects are not just simple data holders. Let's look at the Java String class for example. Yes it holds an immutable string value. But it also has lots of getter-like methods, like length(), substring() and so on.

My classes Pack and Bought are not simple data classes either. They include the unpack() method, which is the only behaviour I require of them. Unit IS a simple data class - it's a tuple. I don't need to tell a Unit to do anything in my problem space; I just need to move it around.

I probably should have put the List of Unit distribution functionality in a separate class, with a constructor taking the List of Unit instances and a method that would return a List of Hamper instances. Instead I chose to create those hampers on the fly, just printing them out as created.

As an aside, I believe a bad design would have been to feed the "packs" array of Bought instances into the hypothetical distribution functionality as that would violate the principle of low coupling. That is, a change in Bought, Pack or Unit could cause a change in the distribution functionality. Whereas with its only "input" being a List of Unit instances, changes to Bought or Pack are irrelevant.

Back to your comment - the point of getters and setters is that THEY DO NOT EXPOSE the internals of your classes. Getters especially, and setters where a mutable object is wanted, encapsulate (hide) the implementation details. All you can claim is that you know some of what is being stored; not how it is stored, nor what kinds of computations are involved.

What seems to bother a lot of people is the idea of automatically generating a getter and a setter for every private field of a class. Of course this isn't necessarily a uniformly good idea - especially setters, as one should always be thinking about whether instances of a class should be immutable or not. Of course it's also possible that a class will have fields that ONLY have meaning internally, and it would be pointless to proved a getter, much less a setter, for that kind of field.

But assuming classes are fit for purpose and they only provide the necessary communication pathways to users, where is the basis for arguing that such limited use of getters and setters are bad?

In my case, since my classes have no setters, you can't make the claim that you can inject any old data into the instances.

Moreover, since you cannot determine from the public methods of my Pack and Bought classes whether I keep a list of the incorporated Unit instances, or whether I just (lazily) materialize that list when unpack() is called, you can't really make the claim that you know all about the internal implementation details of those classes just because I provide you with getters equivalent to what's on the label of the bulk package.

In my Pack or Bought instances, you can argue that my getters are unnecessary since I don't appear to need to know what's inside them. But maybe I want Pack to have the equivalent of a "label" on the bulk package that tells me what's inside without unpacking it, or Bought to tell me how many Units of something I have in total without unpacking all the bulk packages and counting the components. In any case, I don't make use of those methods in this program, so it's certainly valid to say I don't need the getters.

Here's a nice discussion:

https://stackoverflow.com/questions/34805276/do-setter-and-getter-methods-breaks-encapsulation

Belatedly, thanks for your comments, Bernd. I think there's a bit missing from your exposition for example in your class Units your for loop sort of trails off before doing anything. In any case I guess we could debate whether refactoring a compact piece of code like the while loop here actually buys anything.

What I was hoping is that some functional programming wizard would come up with a functional replacement for my while loop (that doesn't modify its input and that is efficient).

Also, with respect, I completely disagree with your comment about "getter methods that would violate information hiding" - the whole point being that there is no maintainability benefit to anyone, neither the person who defines the class nor the persons who use the class later, of exposing the internal implementation details. Instead it's better to define a contract, via getters and setters and perhaps through an interface, that leaves the class maintainer free to change internals as / when necessary.

This is called the "Uniform access principle". You can read about it in many places, for example here:

https://en.wikipedia.org/wiki/Uniform_access_principle

Among many different approaches to lessening the pain of boilerplate getters / setters is Project Lombok, which uses annotations to auto-generate the getters and setters. Baeldung has a nice hands-on overview of this:

https://www.baeldung.com/intro-to-project-lombok