Lives in the UK. Likes blogging, cycling and eating lemon drizzle cake. Roger is a DZone MVB and is not an employee of DZone and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

Defining 'Tell, Don't Ask'... Well, Almost...

03.12.2012
| 5418 views |
  • submit to reddit
I’ve been wanting to write about ‘Tell Don’t Ask’ for some time now, mainly because it’s often espoused as the ‘right’ way to design a program and I've seen lots of Java code that doesn't use it. ‘Tell Don’t Ask’ isn’t a new idea, it’s been around for some time under other guises: in the OO world I’ve seen it called “Objects Do Things” and a long time ago when I was a Windows SDK C++ programmer it was called “Objects Aren’t structs”.

The big idea is that object A tells object B what to do and object B tells C as shown in the diagram below:


...and the idea behind this is that you never interrogate the state of an object in order to make a decision about what to do next - after all, that’s procedural programming.

To demonstrate this, I’m going to use the proverbial shopping cart scenario in which the user adds items to the shopping cart and then gets hold of the items to calculate their total cost.

The item code is very straight forward and looks like this:

public class Item {

  private final String code;
  private final Double price;

  public Item(String code, Double price) {
    this.code = code;
    this.price = price;
  }

  public String getCode() {
    return code;
  }

  public Double getPrice() {
    return price;
  }
}

…whilst the flawed ask don’t tell shopping cart looks like this:

public class ShoppingCart {

  private final List<Item> items;

  public ShoppingCart() {
    items = new ArrayList<Item>();
  }

  public void addItem(Item item) {

    items.add(item);
  }

  public List<Item> getAllItems() {
    return Collections.unmodifiableList(items);
  }
}

All that’s left to do here is to calculate the total code as shown in this unit test:

public class ShoppingCartTest {

  /**
   * Test method for {@link tell_dont_ask.ask.ShoppingCart#getAllItems()}.
   */
  @Test
  public void calculateTotalCost() {

    ShoppingCart instance = new ShoppingCart();

    Item a = new Item("gloves", 23.43);
    instance.addItem(a);

    Item b = new Item("hat", 10.99);
    instance.addItem(b);

    Item c = new Item("scarf", 5.99);
    instance.addItem(c);

    double totalCost = calcTotalCost(instance);
    assertEquals(40.41, totalCost, 0.0001);
  }

  private double calcTotalCost(ShoppingCart instance) {

    List<Item> items = instance.getAllItems();
    double total = 0.0;
    for (Item item : items) {
      total += item.getPrice();
    }

    return total;
  }

}

An that should about wrap things up, the test passes and everyone's happy. Or are they? If you look at the code more closely you’ll see that in order to calculate the total cost the unit test client has to ask the shopping cart to give it a list of its items and then the unit test iterates through them to work out the total. Even though the shopping cart quite rightly returns an immutable list, it is still giving the unit test client code access to its internal state, breaking the rules of encapsulation.

So, this where Tell Don’t Ask comes to the rescue...

public class ShoppingCart {

  private final List<Item> items;

  public ShoppingCart() {
    items = new ArrayList<Item>();
  }

  public void addItem(Item item) {

    items.add(item);
  }

  public double calcTotalCost() {

    double total = 0.0;
    for (Item item : items) {
      total += item.getPrice();
    }

    return total;
  }
}
 
public class ShoppingCartTest {

  /**
   * Test method for {@link tell_dont_ask.ask.ShoppingCart#calculateTotalCost()}.
   */
  @Test
  public void calculateTotalCost() {

    ShoppingCart instance = new ShoppingCart();

    Item a = new Item("gloves", 23.43);
    instance.addItem(a);

    Item b = new Item("hat", 10.99);
    instance.addItem(b);

    Item c = new Item("scarf", 5.99);
    instance.addItem(c);

    double totalCost = instance.calcTotalCost();
    assertEquals(40.41, totalCost, 0.0001);
  }

}

 

In the tell don’t ask version of this code the JUnit client code tells the shopping cart that it wants its total cost and the shopping cart calculates the value, returning it to the caller.

And that’s my tell don’t ask definition in a nutshell, but is it the end of the story? I don’t think so, after this it all becomes more subjective. More on that later. 

 

Published at DZone with permission of Roger Hughes, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Vinodh Lakshmin... replied on Wed, 2012/03/14 - 10:31am

Roger: Thank you for this. Quite simple and I agree with you. However, in this specific example, when we have to list the items in the cart, we would still need the equivalent of getAllItems(), isn't it?

 

Mr Tegg replied on Sun, 2012/03/18 - 5:24am in response to: Vinodh Lakshminarayan

If you need to get all the items then you can always return an iterator to avoid exposing the internal implementation of the class.

Developer Dude replied on Mon, 2012/03/26 - 6:30pm in response to: Mr Tegg

Yes, but since ListIterator implies that the implementation uses a List, you may eventually expose the implementation anyway. Or, you could add the methods that ListIterator has to the class, but then you are basically just wrapping the calls to the iterator and adding little if any value - indeed, you are probably degrading the quality of the class.

Sure, you could return an Iterator instead, and probably should when you can.

But back to the point of the article; are you really telling the object to calculate the total, or are you asking it for the total? It is kind of a matter of semantics - it can be argued that you are asking and not telling. That said, I do agree with the sentiment - to a point - I tend to come from a bit different angle; I often prefer to have objects be value objects (i.e., mostly structs) when I may have different business logic algorithms operate on them.

I may want a total in euros instead of dollars, I may want that to include a VAT value, I may want it to exclude certain items that are not taxable from the tax subtotal, I may want to give an item a discount, or give an item to a customer for no-charge. There are any number of different paths I may want to take depending on context, and I often do not want to embed that context into the object itself.

For example, I may have a telephone number class. I don't necessarily want to tell the tel. # to dial, I may want to tell a phone to dial - after all, I don't want to embed the logic for dialing every possible phone into the telephone # class. But I do probably want the tel. # class to know how validate itself, maybe how to parse a string into itself, maybe know how to output in different formats (or not - I may want a formatter doing that - after all, we have DateFormatter for formatting dates for different locales and different formats, we don't build that into the Date class).

So, it is a matter of judgement when to tell a class something and when to ask it for something.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.