“Feature envy” is a typical code smell in Object Oriented Programming. It can be appreciated when code talking to an object accesses directly to that object’s fields or properties, from the outside, breaking the encapsulation.

// feature envy:
fullName = person.name + " " + person.surname;

// possible solution:
fullName = person.fullName();

The “Tell, don’t ask” principle helps to avoid feature envy when used properly, by using a declarative approach instead of a procedural one. This is, rather than asking a method for a value to make a decision based on the answer, we tell the method to perform an action. Any decisions based entirely upon the state of one object should be made “inside” the object itself. This principle promotes encapsulation, and low coupling. Query methods (method returning values) can’t be totally avoided, in fact they shouldn’t, but they can be pushed down to places where coupling is affordable, thus avoiding coupling high level classes, reducing the impact of changes.

“Tell, don’t ask”, and “Law of Demeter” were explained  by Andy Hunt and Dave Thomas in a IEEE Software column and on their site. Also recently by Martin Fowler.

Recently, I came across an “extract method” refactoring promoting this principle that makes the code easier to read and maintain:

Before:

// Service class:
...
public virtual void RequestPin(User user) {
   var newPin = PinBuilder.CreateNewPin();
   PinRepository.SaveNewPinFor(newPin, user.UniqueIdentifier);
   SmsProxy.SendSms(user.Phone, CreateSMSMessage(newPin));
}
...

After:

// Service class:
...
public virtual void RequestPin(User user) {
   var newPin = PinBuilder.CreateNewPin();
   user.ResetPIN(newPin, PinRepository);
   user.RequestSMS(CreateSMSMessage(newPin), SmsProxy);
}
...

// User class:
...
public void ResetPIN(string newPin, PinRepository pinRepository) {
   pinRepository.SaveNewPinFor(UniqueIdentifier, newPin);
}

public void ReceiveSMS(string message, SmsProxy smsProxy) {
      smsProxy.SendSms(Phone, message);
}
...

This refactoring brings several advantages. First, we remove feature envy, because properties “UniqueIdentifier” and “Phone” are used inside the User object. So the data and the behavior are in the same place. If I have to change the field in the future, it will be clear what is it affecting to. Second, the service code is easier to read and encapsulates details that should not be visible there.

It looks weird at first, sending a repository or a service instance as a parameter to an entity’s method. I usually do it as I refactor, not upfront. The tests don’t need to be changed if they are well-written.