Referencing another object’s state directly couples two objects
together based on what they are, rather than on what they
do. By following tell, don’t ask, we
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.
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
Public methods are a liability. Before they can be changed, moved,
renamed or removed, you will need to find every consumer class and
update each one accordingly.
This principle can be difficult to follow while also following
Consider a view that uses the above Order model:
<%= form_for @orderdo|form|%><%unless current_user.has_valid_credit_card?%><%=render'credit_card/fields', form: form%><%end%><!-- Order Fields --><%end%>
The view doesn’t display the credit card fields if the user already
has a valid credit card saved. The view needs to ask the user a question
and then change its behavior based on that question, violating tell, don’t ask.
You could obey tell, don’t
ask by making the user know how to render the credit card form:
<%= form_for @orderdo|form|%><%= current_user.render_credit_card_form%><!-- Order Fields --><%end%>
However, this violates MVC by including view logic in the
User model. In this case, it’s better to keep the model,
view and controller concerns separate and step across the tell, don’t ask line.
When writing interactions between other models and support classes,
though, make sure to give commands whenever possible and avoid
deviations in behavior based on another class’s state.