Understanding the "Tell, Don't Ask" Principle Through Real-World Scenarios in C#
In today's article, I will cover one particular programming concept which would allow you to tidy up code, making it more effective.
The concept is "Tell. Don't Ask". To do that, we would illustratively demonstrate the concept through a metaphor in the world of banking which should make understanding easy for you. I'll break it down in simple terms, and we'll explore diagrams and C# code snippets to make it all come alive.
Scenario with Banking Business
Let's say you're developing a banking application. You have a BankAccount class that contains information like balance, account number, and so on.
Traditional Approach - Ask, then Tell
In this method, you ask the BankAccount for its balance and then decide whether to proceed with the withdrawal or not.
public class BankAccount
{
public decimal Balance { get; private set; }
public void Withdraw(decimal amount)
{
if (Balance >= amount)
{
Balance -= amount;
}
else
{
throw new Exception("Insufficient funds");
}
}
}
// Client code
BankAccount account = new BankAccount();
if (account.Balance >= 100)
{
account.Withdraw(100);
}
Modern Approach - Tell, Don't Ask
Here, you directly tell the BankAccount to handle the withdrawal. The account itself is responsible for checking if it has sufficient funds.
public class BankAccount
{
public decimal Balance { get; private set; }
public void Withdraw(decimal amount)
{
if (CanWithdraw(amount))
{
Balance -= amount;
}
else
{
throw new Exception("Insufficient funds");
}
}
private bool CanWithdraw(decimal amount)
{
return Balance >= amount;
}
}
// Client code
BankAccount account = new BankAccount();
account.Withdraw(100);
When should we consider the principle "Tell, Don't Ask"?
Encapsulation
When you follow the "Tell, Don't Ask" principle, fundamentally you're giving ownership of managing it's inner state to the object. This enhances encapsulation in the way you're not exposing the internals of the object. Instead, you are instructing the object to take care of its duties like a well-trained dog knowing when to sit, stay or fetch.
Readability
Code following the principle of "Tell, Don't Ask" usually is the one easy to read. Indeed, it reduces your cognitive load in order to understand what code will do next. You don't have to keep track of many conditions or states, you simply state what you want the object to do and it takes care of that on its own. It's like reading a well-written book the story flows naturally without having you jump back and forth between pages.
Maintainability
If the behavior is encapsulated into the object, any future changes made in that behavior will require changes to take place only at one location. This creates a codebase that can be maintained and fewer bugs occur. Imagine you have a car; if all the controls are in one place (the dashboard), it's easier to operate and fix. You do not have to go to different parts of the car to turn on the headlights, adjust the air condition or tune radio.
Reduced Complexity
By telling an object what to do, you localize the behavior and hence reduce the complexity. You don't have to write complex client code which has to handle various states and conditions. The object itself takes care of that making the overall system simpler.
For the end
The "Tell, Don't Ask" principle is handy as a guiding code rule that brings several benefits in the perspective of software development. It helps to have an exclusive way for enhancing the concept of encapsulation enabling objects to manage their own state as well as discouraging accessing internals. This thus results to a more readable and maintainable code, since you don't have to dig in the state of an object so as to understand its responsibility. Also, it invites the flexible code there is easy change and extension of the code in near future. Overall, adherence to "Tell, Don't Ask" can highly enhance your quality of code base.
Cheers! 👋