DEV Community

JongHwa
JongHwa

Posted on

Stop Asking for Data! Master the "Tell, Don't Ask" Principle

One of the most common mistakes in Object-Oriented Programming (OOP) is treating objects like simple data structures. We often pull data out of an object, perform some logic, and then put the result back in.
This violates the "Tell, Don't Ask" (TDA) principle.

In this post, I will explain what TDA is and how it improves your code using a simple Shop and Account example.

What is "Tell, Don't Ask"?

The principle is simple:

  • Don't Ask: Don't ask an object for its data to perform logic on it yourself.
  • Tell: Tell the object what to do, and let it handle its own state.

When you follow TDA, you improve Encapsulation and Cohesion.


The Problem: Procedural Code in OOP ❌

Let's look at a common anti-pattern. Here, the Shop class is managing the logic that belongs to the Account.

package com.mateandgit.programming.chapter1_2.problem;

public class Shop {

    public void sell(Account account, Product product) {
        // ❌ BAD: Asking for data (getMoney)
        long price = product.getPrice();
        long mileage = account.getMoney();

        // ❌ BAD: The Shop is deciding if the account has enough money
        if (mileage >= price) {
            // ❌ BAD: Modifying the account's state externally
            account.setMoney(mileage - price);
            System.out.println(product.getName() + " purchased.");
        } else {
            System.out.println("Insufficient balance.");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Why is this bad?

  • Violation of Encapsulation: The Shop knows too much about how the Account works (it knows it has "money" and knows how to calculate the balance).
  • Logic Duplication: If another class (e.g., SubscriptionService) needs to deduct money, you have to duplicate this if (balance >= price) logic there too.
  • Hard to Maintain: If the validation rule changes (e.g., "minimum balance must remain 100"), you have to fix it in every class that accesses Account.

The Solution: Tell the Object What to Do ✅

Refactoring this to follow TDA means moving the logic into the Account class. The Shop should just command the Account to pay.

package com.mateandgit.programming.chapter1_2.solution;

public class Shop {

    public void sell(Account account, Product product) {
        // ✅ GOOD: Just asking a question (canAfford)
        if (account.canAfford(product.getPrice())) {

            // ✅ GOOD: Telling the object to do something (withdraw)
            account.withdraw(product.getPrice());
            System.out.println(product.getName() + " purchased.");
        } else {
            System.out.println("Insufficient balance.");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

(Inside the Account class, you would have methods like canAfford() and withdraw() that encapsulate the logic.)

Why is this better?

  • Better Encapsulation: The Shop doesn't know how the withdrawal happens. It just sends a message.
  • Reusable Logic: The validation rules (canAfford) are centralized in the Account class.
  • Readability: The code reads like a sentence: "If account can afford, withdraw."

Conclusion

Object-Oriented Programming is about sending messages, not manipulating data.
If you find yourself writing getSomething(), changing it, and calling setSomething(), stop and think:

"Should this logic belong to the object I'm getting data from?"

Apply the Tell, Don't Ask principle to keep your objects independent and your logic clean.

Key Takeaways

  • Don't: get data -> calculate -> set data.
  • Do: Create a method in the object and tell it to perform the action.
  • Result: High cohesion, low coupling, and better encapsulation.

Top comments (0)