Tell, Don't Ask
Introduction#
In object-oriented programming, one of the most fundamental principles for maintaining proper encapsulation is “Tell, Don’t Ask.” This principle states that you should tell objects what you want them to do rather than asking them for data and then making decisions externally. While seemingly simple, this guideline has profound implications for code design, maintainability, and the integrity of object-oriented systems.
The Principle Defined#
Tell, Don’t Ask can be summarized as:
“Tell objects what you want them to do; don’t ask them for data and make decisions for them.”
This principle was popularized by Andy Hunt and Dave Thomas in “The Pragmatic Programmer” and reflects a core tenet of object-oriented design: objects should be responsible for their own behavior and data manipulation.
For software developers, “telling” means calling methods that perform actions, while “asking” means calling getters to retrieve data and then making decisions based on that data outside the object.
The Problem: Asking Instead of Telling#
When we ask objects for their data instead of telling them what to do, we create several problems:
// Asking (violation of Tell, Don't Ask)
if (account.getBalance() > amount) {
account.setBalance(account.getBalance() - amount);
transaction.setStatus("completed");
} else {
transaction.setStatus("failed");
}
This approach has several issues:
1. Broken Encapsulation#
The calling code needs to know about the internal structure and business rules of the Account object. This exposes implementation details that should be hidden.
2. Scattered Business Logic#
The logic for handling withdrawals is spread across multiple locations instead of being centralized in the Account class where it belongs.
3. Procedural Code#
This style of programming is essentially procedural programming disguised as object-oriented code—we’re manipulating data structures rather than leveraging object behavior.
The Solution: Telling Objects What to Do#
The Tell, Don’t Ask approach encapsulates behavior within objects:
// Telling (proper encapsulation)
WithdrawalResult result = account.withdraw(amount);
transaction.setStatus(result.getStatus());
Or even better, with full encapsulation:
// Complete encapsulation
account.processWithdrawal(amount, transaction);
This approach provides several benefits:
1. Proper Encapsulation#
The Account object maintains control over its data and the business rules that govern its behavior. External code doesn’t need to know how withdrawals are processed.
2. Centralized Logic#
All withdrawal logic is contained within the Account class, eliminating duplication and ensuring consistency across the application.
3. Easier Maintenance#
Changes to withdrawal rules only require modifications in one place: the Account class’s withdraw method.
Real-World Applications#
Domain Logic#
Objects should encapsulate business rules rather than exposing their internal state for external manipulation:
// Don't ask for order state and modify externally
orderReporter.getItems().add(newItem);
orderReporter.setTotal(orderReporter.getTotal() + newItem.getPrice());
// Tell the order reporter to add an item
orderReporter.addItem(newItem);
User Interface Components#
UI components should provide behavior-focused methods rather than exposing internal state:
// Don't manipulate component state directly
component.getState().setVisible(false);
component.getState().setEnabled(false);
// Tell the component what to do
component.hide();
component.disable();
Service Layer Design#
Services should provide high-level operations rather than exposing internal workflow steps for external orchestration.
Relationship to Other Principles#
Tell, Don’t Ask reinforces several other object-oriented principles:
Law of Demeter#
Tell, Don’t Ask naturally supports the Law of Demeter by encouraging objects to communicate through behavior rather than reaching through multiple objects to access distant data.
Single Responsibility Principle#
When objects are responsible for their own behavior, they naturally develop more focused, cohesive responsibilities rather than being passive data containers.
Open/Closed Principle#
Objects with well-encapsulated behavior are easier to extend through inheritance or composition without modifying existing code.
Common Misconceptions#
”Never Use Getters”#
Tell, Don’t Ask doesn’t mean eliminating all getters. It means avoiding the pattern of getting data solely to make decisions that the object should make itself. Simple property access for display purposes is often acceptable.
”Always Hide Everything”#
Some objects are legitimately data containers (DTOs, value objects, configuration classes). The principle applies primarily to objects with significant behavior and business logic.
”Methods Must Never Return Data”#
Methods can return data when that data represents the result of a computation or query. The key is avoiding the pattern where the returned data is immediately used to make decisions that the original object should handle.
Implementation Strategies#
1. Focus on Behavior#
Design object interfaces around what objects can do rather than what data they contain. Think in terms of responsibilities and capabilities.
2. Use Command Methods#
Provide methods that perform complete operations rather than exposing the individual steps. This encapsulates the workflow within the appropriate object.
3. Return Results, Not Raw Data#
When methods must return information, return result objects that encapsulate both data and behavior rather than primitive values that require external interpretation.
4. Use Events for Coordination#
When objects need to coordinate, consider using events or observers rather than having one object manipulate another’s state directly.
Benefits and Trade-offs#
Following Tell, Don’t Ask provides several benefits:
- Better encapsulation: Objects maintain control over their data and behavior
- Reduced coupling: Clients depend on behavior contracts, not internal structure
- Improved maintainability: Changes to object internals don’t affect external code
- Enhanced testability: Behavior-focused objects are easier to test and mock
However, there are trade-offs to consider:
- Interface complexity: Objects may need more methods to support all use cases
- Performance considerations: Method calls may have more overhead than direct data access
- Learning curve: Requires thinking differently about object design and responsibility
Conclusion#
Tell, Don’t Ask represents a fundamental shift from procedural to truly object-oriented thinking. By telling objects what to do rather than asking for their data, we create systems that are more maintainable, flexible, and aligned with object-oriented principles.
The principle encourages proper encapsulation and helps developers think about objects as autonomous entities with their own responsibilities rather than as passive data structures. While it requires discipline and careful design, the resulting code is typically more robust and easier to evolve over time.
As “The Pragmatic Programmer” emphasizes, good object-oriented design is about assigning responsibilities appropriately. Tell, Don’t Ask is one of the most effective guidelines for ensuring that objects truly own their behavior rather than being manipulated by external code.
The next time you find yourself getting data from an object to make a decision, ask yourself: should this object be making this decision itself? Often, the answer is yes, and Tell, Don’t Ask shows you the better way.