Tell, Don’t Ask

Notes

The Tell, Don’t Ask principle advises developers to tell objects what you want done, rather than querying objects and making decisions for them.

Eg:

class Order
  def charge(user)
    if user.has_valid_credit_card? 
	    user.charge(total)
    else
      false
    end
  end
end

This example doesn’t follow Tell, Don’t Ask. It first asks the user if it has a valid credit card, and then makes a decision based on the user’s state.

In order to follow Tell, Don’t Ask, we can move this decision into User#charge:

class User
  def charge(total)
    if has_valid_credit_card?
      payment_gateway.charge(credit_card, total)
      true
    else
      false
    end
  end
end
 
class Order  
	def charge(user)
		user.charge(total)
	end 
end

Benefits

Encapsulation of Logic

Following Tell, Don’t Ask encapsulates the conditions under which an operation can be performed in one place. In the above example, User should know when it makes sense to charge.

Encapsulation of State

Referencing another object’s state directly couples two objects together based on what they are, rather than just what they do. By following Tell, Don’t Ask, you encapsulate state within the object that uses it, exposing only the operations that can be performed based on that state, and hiding the state itself within private methods and instance variables.

Minimal Public Interface

In many cases, following Tell, Don’t Ask will result in the smallest possible public interface between classes. In the above example, has_valid_credit_card? can now be made private, because it becomes an internal concern encapsulated within User.

Public methods are a liability. Before they can be changed, moved, renamed, or removed, you need to find every consumer class and update them accordingly.

Tension with MVC

Application

These smells may be a sign that you should be following Tell, Don’t Ask more:

  • Feature Envy⭐ is frequently a sign that a method or part of a method should be extracted and moved to another class, reducing the number of questions that method must ask of another object.
  • Shotgun Surgery⭐ may result from state and logic leaks. Consolidating conditionals using Tell, Don’t Ask may reduce the number of changes required for new functionality.

If you find classes with these smells, they may require refactoring before you can follow Tell, Don’t Ask:

  • Case Statement⭐ that inflect on methods from another object generally get in the way.
  • Mixin blur the lines between responsibilities, as mixed in methods operate on the state of the objects they’re mixed into.

Many Law of Demeter violations point towards violations of Tell, Don’t Ask. Following Tell, Don’t Ask may lead to violations of the Single Responsibility Principle and the Open Closed Principle, as moving operations onto the best class may require modifying an existing class and adding a new responsibility.