What if I told you Morpheus never said: “What if I told you?” I know you saw that meme a thousand times but have you actually heard him say it in the movie? You didn’t, because it didn’t happen. Darth Vader also never said “Luke, I am your father” and Captain Kirk never said, “Beam me up, Scotty”. People keep repeating these “quotes” so we assume they are true.
Cargo Cult
This phenomenon is not limited to spoken word — it afflicts human behaviors also. Since software is developed by humans (for now), we come across it in software development. It’s called cargo cult programming and Wikipedia defines it as:
“Ritual inclusion of code or program structures that serve no real purpose.”
One practice that is widespread among developers is the creation of getters and setters. They always come together and we create them as soon as the entity is created. This is so deeply engrained in our workflow that nowadays IDEs offer to automatically generate these getters and setters for us.
But is there a reason for doing this or is it just a ritual we have accepted without stopping to wonder why?
Set the lights to off
Let’s look at the setters first. By setters I mean methods like this:
public function setName(string $name)
{
$this->name = $name ;
}
This method clearly changes the name of the object in question. In the real world, we call this renaming. I have never heard someone say that a person has “set its name to another name”. Even if you ask the developer who wrote the method chances are they will tell you that it renames the object. So, here’s an interesting proposition for you: if renaming is what the method does, call it rename.
public function rename(string $name)
{
$this->name = $name ;
}
Now our method name describes precisely what our method does and we used a domain-specific word for it. The (domain) concept of renaming a user is reflected in our code. This gives developers and non-developers a common (ubiquitous) language when discussing the project.
Let’s look at another example. Say we have a Subscription
class with the following setter method:
public function setActive(bool $active): void
{
$this->active = $active;
}
As with the previous example, this method doesn’t clearly reflect the domain concept which is activating and deactivating a subscription. So we split it into:
public function activate(): void
{
$this->active = true;
}
public function deactivate(): void
{
$this->active = false;
}
Again we have expressed these concepts explicitly in our code; furthermore, there is another benefit gained by this refactoring. Having a setter method allowed the outside world to set the internal state of the Subscription
object, whereas now, the Subscription
object is responsible for setting its own internal state.
Anemic vs. Rich
By having myriad setter methods on your entity, you end up with what’s called the anemic domain model.
<?php
class User {
public function setName(string $name): void
//...
public function setBirthDate(DateTime $date): void
//...
public function setAddress(Address $name): void
//...
}
Your User
entity is more of a data structure than an object. Data structures expose their data and have little or no behavior while objects protect their data and expose behaviors.
To have a richer domain model, we refactor this to:
class User {
public function __construct(string $name, DateTime $birthDate, Address $address)
{
$this->name = $name;
$this->date = $birthDate;
$this->address = $address;
}
public function rename(string $name): void
//...
public function updateAddress(Address $address): void
//...
}
Now our User class is protecting its internal state (invariants) and exposing only behaviors to the outside world.
“Get me your name”
Now, to the getters. From my experience, this is harder for people to get (pun intended), and, to be clear, by getters I mean methods like this:
public function getName() : Name
{
return $this->name;
}
This method returns the name of the object in question. Many developers would say this is clear from its name. The get + Name tells us it returns the Name. Actually, the “get” prefix guarantees nothing. In actuality, it is the return type of the method that guarantees this method returns a Name.
If you ditch the get prefix in the method name then you lose no information in the process:
public function name() : Name
{
return $this->name;
}
If you can remove a word without losing any information, then that word is just noise.
But this is how we talk, right? Not really. Recall from memory a situation where you need to reveal some personal information about yourself such as being in a bank. When the bank clerks ask you for your address, do they say “Get me your address” or “Get me your name”? They don’t.
In fact, when we use the word get in real life it usually isn’t a question but an imperative that has some implied side effect. Say you want a beer so you tell your friend “Hey, get me a beer from the fridge”. The side effect of him getting you a beer is that the freezer will have one less beer afterward. This will happen every time you call him to get you a beer. I’ll admit, it would be nice if beer fridges would follow our programming conventions of “getters” not changing the internal state of the object, but they don’t.
Naming things in software development is hard and we want to be as precise as possible when doing it. What if I told you get and set are among the most Ambiguous words in the English language. Check google and see how long the list of their possible meanings is.
Maybe they aren’t the best candidates when you’re aiming for precision.
Top comments (4)
Interesting article. Do you think it’s better (clearer, cleaner, etc) to have 2 separate
activate
anddeactivate
methods or one methodupdateStatus
that takes a boolean argument?Yes, I prefer
activate
anddeactivate
. It's no setting boolean only. You may propagate this state into dependencies.Hello Doaa,
Yes, that would be in line with what I'm recommending in this article.
I am a getter and a setter hater. I hate anemic domain model. I prefer immutable objects with no setters. The same problem with getters. I put behavior into object and I am happy.
Tell, don't ask!