Why (not) need getters?

The previous article about setters / getters as a way to work with an entity (using Symfony in PHP as an example) received a heated discussion. In this article I will try to express my thoughts separately about getters: why and when to get something, what responsibility they decide and when it is appropriate to use them, when it is not appropriate. I tried to collect thoughts in one place and formalize them with you.

getHumanFactory (). getHuman (). getDoneWork (). getWorkTime ()


Getters are needed in order to get some state of the current object. In OOP languages, this is the value of some class variable, usually private.

class Article {
     private String name

     public function getName(): String {
          return this.name
     }
}

The value of the class variable itself can be obtained as you wish. We simply ask the object to give us what it is, the very name of the method says “give” to us: but do not “do”, do not “send”, do not “create”, do not “count”. Says give - give what is. This works quite normally in all kinds of data objects, the responsibility of which is to just give / carry information.

There can be no logic in the getter, because the semantics of the word are straightforward. "Mom, give me a pie " does not contain "Mom, buy flour, fire a pie and finally give me a pie ." Maybe the phrase “provide me with a pie” somehow can encapsulate all these actions, but it’s definitely not “give”.

You will be surprised, but I have met this way of naming all methods more than once. The complexity of the code at the same time grows quite a bit, because in the getter chains it is not always clear what exactly is happening in the application layers and what kind of connections are involved, as a rule, not everything is in order with the design and connivance of this kind is a source of errors.

getValue () throw WhyDoYouNeedIt? {}


The purpose of the data object is understandable - this is the corporal from the 1917 film, who is fleeing somewhere to convey some message about the need to retreat.

But what to do with business objects? Why does the entity “Document” give someone a list of their fields?

If this is a business object, then the document can be posted, rejected, verified (including in these fields), filled out or confirmed.

If there is a need to "give" the field, then this document is not part of the business process. Why do I need to give someone a field? Maybe for publication? Or extract, or archiving, or sending a copy by mail, or report? It’s not entirely clear why and to whom - there is a separate responsibility to “give” like that of a good old data object, you can clearly see the use in the form of some source for reading in the context of another business process that is not basic in understanding the document itself.

doEverythingEverywhere (world.getGod ())


Back to the business entities. If you leave getters in entities, then the temptation is great to use its getters exactly everywhere and as you like. We are tied to the state of a certain object and we reverse absolutely any logic to the campaign. It may even seem that we have not broken encapsulation. But what about? State in one place, behavior in another - a classic encapsulation violation.

For example, there is some entity:

class Order
{
     private Status status
     private Boolean closed

     public function getStatus(): Status {
          return this.status
     }

     public function setClosed(Boolean closed): void {
           this.closed = closed
     }
}

Surely, with such an architecture, you will request a status in a dozen / a hundred places. It can be all sorts of services, controllers, other modules. I saw only once when restrictions were artificially created for the distribution of this code by some set of rules for developers, not code ... Encapsulation was provided by coding standards, and not by code design :).

If you needed to do something like this somewhere:

if(order.getStatus() == input.getStatus()) {
      order.setClosed(true)
}

That most likely the entity does not contain a state machine. This means that the invariants of the object from the inside are not controlled in any way - there is no verification of data during each operation from the inside. As a result - high connectivity, complex functional testing, since it is not enough to check the unit logic of the code that changes the state of the entity, you need to check that the state of the entity external to the code is valid. And as a result: increased complexity, probability of bugs, more code and complex tests.

Bottom line: I hope my and your colleagues will use getters less like any method that does any work with the result that will return.

And I hope more developers will pay attention to the CQRS concept, where the responsibilities for reading and business operations are divided.

Good to all!

All Articles