Tell-don’t-ask, please!
Are we doing Object-Oriented Programming (OOP language) because we are using some fancy things from an OOP language?. I don’t think so, you…
Are we doing Object-Oriented Programming (OOP language) because we are using some fancy things from an OOP language?. I don’t think so, you can write code in Java and not doing at all OOP. The key is encapsulation.
In object-oriented programming (OOP), encapsulation refers to the bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object’s components. Encapsulation is used to hide the values or state of a structured data object inside a class, preventing direct access to them by clients in a way that could expose hidden implementation details or violate state invariance maintained by the methods.
wikipedia
With this definition in mind in OOP, we should reduce the use of getters. If you don’t use getters, but you have records or similar concepts to easily access properties inside objects, this also applies.
Getters are a way to show the values of an object outside the object itself. The effect of getters is like centrifugal force, the logic is moved outside the class that should contain it.
Let’s use this example to talk about it:
My getters have moved the logic to calculate the new position inside the “PositionDirectionService”. This is what we call an anemic model, the domain classes have no logic and the logic is inside a pure fabricated class.
Let’s use Tell Don’t Ask to refactor this code:
Tell-Don’t-Ask is a principle that helps people remember that object-orientation is about bundling data with the functions that operate on that data. It reminds us that rather than asking an object for data and acting on that data, we should instead tell an object what to do. This encourages to move behavior into an object to go with the data.
Martin fowler
To remove the getters, I have to create a method inside the classes in charge of calculating the new position (Position and Vector). In my mind, a vector can be applied to a position, so I created a method to apply to Position a Vector then I call Vector to calculate the new x and y without exposing those values to the rest of the world.
I don’t break encapsulation, and I’m also moving the methods to the classes that contain the data.
Information expert (also expert or the expert principle) is a principle used to determine where to delegate responsibilities such as methods, computed fields, and so on.
Using the principle of information expert, a general approach to assigning responsibilities is to look at a given responsibility, determine the information needed to fulfill it, and then determine where that information is stored.
This will lead to placing the responsibility on the class with the most information required to fulfill it.
GRASP information expert
Then all getters are bad?
Short answer no:
Very very short summary: It is okay to use accessors to get the state of an object, as long as you don’t use the result to make decisions outside the object. Any decisions based entirely upon the state of one object should be made ‘inside’ the object itself.
A clarification of the LawOfDemeter.
Just a clarification, if you are thinking then how can I paint a JSON using Jackson library in Java?.
The answer is using “getters” but that’s not your decision, that’s the decision of the people who create the Jackson library. Don’t make that decision that is outside your control to decide everything in your code. The same happen with any other tool, library or framework to play with other systems.
Just because a library to paint JSON or a framework adapts concepts to solve their problem to a language that does not have that concept, don’t move that decision also to your Domain code.
In Java for example there is no artifact to represent a JSON, so multiple libraries use classes as a kind of fixed dictionary, but those are not our domain classes. Those classes are artifacts coupled to the library or framework we are using, please don’t move them inside your domain.
Following the Java example any framework like JPA, Hibernate or even things like Quarkus or Springboot do the same, please write a thin layer to play with them in those things that you need to use them.
If you don’t know how to avoid this, please take a look at my previous post about the Why’s of hexagonal architecture.
Layered architectures are there to help you to avoid passing those decisions to the rest of your code. They are there to avoid being cannibalized by others decisions, use them.
Refactor
I want to emphasize that I’m not saying that you have to create code from the first time without getters. Abstractions are not an easy thing to find, I prefer to recommend writing the code as you usually do.
There are small steps that can help you to see the problem easily. You can:
Extract methods with few lines of code, less Cognitive Load.
If you see that the method is only used by your class and only plays with the parameters passed (don’t do anything with the attributes of the class), perhaps that method is not in the correct class.
Then take a look at the method, is calling getters to do a calculation with the values of the parameters?.
Then ask yourself
Why is this getter here?
What can I do to remove it?
Sometimes the answer is just apply the “Tell Don’t Ask” principle.