Tell, don't ask principle here is often pasted to me when I use getters or setters, and people tell me not to use them. The site clearly explains what I should and what I shouldn't do, but it doesn't really explain WHY I should tell, instead of asking. I find using getters and setters much more efficient, and I can do more with them.
Imagine a class Warrior
with attributes health
and armor
:
class Warrior {
unsigned int m_health;
unsigned int m_armor;
};
Now someone attacks my warrior with a special attack that reduces his armor for 5 seconds. Using setter's it would be like this:
void Attacker::attack(Warrior *target)
{
target->setHealth(target->getHealth() - m_damage);
target->setArmor(target->getArmor() - 20);
// wait 5 seconds
target->setArmor(target->getArmor() + 20);
}
And with tell, don't ask principle it would look like this (correct me if i'm wrong):
void Attacker::attack(Warrior *target)
{
target->hurt(m_damage);
target->reduceArmor(20);
// wait 5 seconds
target->increaseArmor(20);
}
Now the second one obviously looks better, but I can't find the real benefits of this.
You still need the same amount of methods (increase
/decrease
vs set
/get
) and you lose the benefit of asking if you ever need to ask.
For example, how would you set warriors health to 100?
How do you figure out whether you should use heal
or hurt
, and how much health you need to heal or hurt?
Also, I see setters and getters being used by some of the best programmers in the world. Most APIs use it, and it's being used in the std lib all the time:
for (i = 0; i < vector.size(); i++) {
my_func(i);
}
// vs.
vector.execForElements(my_func);
And if I have to decide whether to believe people here linking me one article about telling, not asking, or to believe 90% of the large companies (apple, microsoft, android, most of the games, etc. etc.) who have successfully made a lot of money and working programs, it's kinda hard for me to understand why would tell, don't ask be a good principle.
Why should I use it (should I?) when everything seems easier with getters and setters?
Getters and setters are used to protect your data, particularly when creating classes. For each instance variable, a getter method returns its value while a setter method sets or updates its value. Given this, getters and setters are also known as accessors and mutators, respectively.
The getter and setter method gives you centralized control of how a certain field is initialized and provided to the client, which makes it much easier to verify and debug. To see which thread is accessing and what values are going out, you can easily place breakpoints or a print statement.
You may use lombok - to manually avoid getter and setter method. But it create by itself. The using of lombok significantly reduces a lot number of code.
Yes, the methods of your class should call the getters and setters. The whole point in writing getters and setters is future proofing. You could make every property a field and directly expose the data to the users of the class.
You still need the same amount of methods (increase/decrease vs set/get) and you lose the benefit of asking if you ever need to ask.
You got it wrong. The point is to replace the getVariable
and setVariable
with a meaningful operation: inflictDamage
, for example. Replacing getVariable
with increaseVariable
just gives you different more obscure names for the getter and setter.
Where does this matter. For example, you don't need to provide a setter/getter to track the armor and health differently, a single inflictDamage
can be processed by the class by trying to block (and damaging the shield in the process) and then taking damage on the character if the shield is not sufficient or your algorithm demands it. At the same time you can add more complex logic in a single place.
Add a magic shield that will temporarily increase the damage caused by your weapons for a short time when taking damage, for example. If you have getter/setters all attackers need to see if you have such an item, then apply the same logic in multiple places to hopefully get to the same result. In the tell approach attackers still need to just figure out how much damage they do, and tell it to your character. The character can then figure out how the damage is spread across the items, and whether it affects the character in any other way.
Complicate the game and add fire weapons, then you can have inflictFireDamage
(or pass the fire damage as a different argument to the inflictDamage
function). The Warrior
can figure out whether she is affected by a fire resistance spell and ignore the fire damage, rather than having all other objects in the program try to figure out how their action is going to affect the others.
Well, if that's so, why bother with getters and setters after all? You can just have public fields.
void Attacker::attack(Warrior *target)
{
target->health -= m_damage;
target->armor -= 20;
// wait 5 seconds
target->armor += 20;
}
The reason is simple here. Encapsulation. If you have setters and getters, it's no better than public field. You don't create a struct here. You create a proper member of your program with defined semantics.
Quoting the article:
The biggest danger here is that by asking for data from an object, you are only getting data. You’re not getting an object—not in the large sense. Even if the thing you received from a query is an object structurally (e.g., a String) it is no longer an object semantically. It no longer has any association with its owner object. Just because you got a string whose contents was “RED”, you can’t ask the string what that means. Is it the owners last name? The color of the car? The current condition of the tachometer? An object knows these things, data does not.
The article here suggests here that "tell, don't ask" is better here because you can't do things that make no sense.
target->setHealth(target->getArmor() - m_damage);
It doesn't make sense here, because the armor has nothing in relation to health.
Also, you got it wrong with std lib here. Getters and setters are only used in std::complex
and that's because of language lacking functionality (C++ hadn't had references then). It's the opposite, actually. C++ standard library encourages usage of algorithms, to tell the things to do on containers.
std::for_each(begin(v), end(v), my_func);
std::copy(begin(v), end(v), begin(u));
One reason that comes to mind is the ability to decide where you want the control to be.
For example, with your setter/getter example, the caller can change the Warrior's health arbitrarily. At best, your setter might enforce maximum and minimum values to ensure the health remains valid. But if you use the "tell" form you can enforce additional rules. You might not allow more than a certain amount of damage or healing at once, and so on.
Using this form gives you much greater control over the Warrior's interface: you can define the operations that are permitted, and you can change their implementation without having to rewrite all the code that calls them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With