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

Disassembling 'Tell Don't Ask' - Internal State of the Object

03.16.2012
| 4157 views |
  • submit to reddit

In my last blog I defined Tell Don’t Ask (TDA) using a simple shopping cart example. In it the shopping cart was responsible for working out the total cost of the items in the cart as opposed to the client asking for a list of items and then calculating the total cost itself. The TDA example is shown below:

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;
  }
}

...and this is the test case:

  @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);
  }

I have to ask though, does TDA it all come down to a matter language and semantics. Consider the example above, is the client telling or asking? Although this code is much better than my ask don't tell example, I think that a case can be made for saying that the client is asking for the total price. Consider the following issues:

  • In telling the Shopping Cart to return total price how do you know that you’re not querying the internal state of the object? Looking at the ShoppingCart code, you can see that the total cost is not part of the object’s direct internal state1, but the object calculates it and hence the total price is part of the object’s derived internal state and this is being returned to the caller.
  • In a TDA world, why would the client need the total cost? To figure out if you need to add a shipping cost? That can be done by the ShoppingCart. To submit the bill to the appropriate payment system? Again that can be done by the shopping cart.

If you agree that return values reflect the internal state of an object, whether direct or inferred then, as Mr Spock would say, logic dictates that you’d have to conclude that all method signatures would have a void return value, never throw exceptions and handle all errors themselves and look something like this:

  public void processOrder(String arg1, int arg2);

And this is where some of the logic can start to unravel. Doesn’t a ShoppingCart contacting the Visa or Mastercard payment system break the single responsibility principle (SRP)? The WareHouse also needs to be informed that the order can be shipped. Is that the responsibility of the ShoppingCart? And what if we wanted to print an itemised bill in PDF format? You can tell the ShoppingCart to print itself, but are we breaking the SRP by adding printing code to the ShoppingCart however small it may be? Perhaps this requires further thought, so take a look at the Communication Diagram below:


This diagram shows the straight forward scenario of adding an item to the ShoppingCart. TDA works perfectly here because there are no branches and no decisions, the whole process is linear: the browser TELLS the OrderController to add an item and the OrderController TELLS the ShoppingCart to add an item to itself.

Now, take a look at the next scenario. Here the user is confirming the order and then sitting down to eagerly await its delivery.


If you look at the diagram, you’ll see that the browser TELLS the OrderController to confirm the order. The OrderController TELLS the ShoppingCart to confirm the order. The ShoppingCart TELLS the Visa system to charge the total to the user’s card. If the the payment goes through okay then the Visa card TELLS the WareHouse to ship the order.

Now this works as a design if you accept that a ShoppingCart is responsible for maintaining a list of items the user wants and paying for those items. You’ve also got to accept that the Visa card is responsible for shipping them to the customer: all of which doesn’t make sense to me as it breaks the SRP and, in my opinion, the SRP is one of the fundamental features of good software design. Besides, when I’m doing my shopping in the supermarket I don’t ask my shopping cart to pay the bill, I have to get my wallet out. If you take that analogy further, then you’ll conclude that it’s some other object's responsibility to marshall the flow of the transaction; the OrderController’s perhaps?


In this final diagram you can see that the browser TELLS the OrderController to confirm the order. The OrderController ASKS the ShoppingCart for the total cost and then TELLS the Visa object to pay the bill and finally if the Visa returns success the it TELLS the WareHouse to ship the ShoppingCart. To me this design makes more sense, it’s tell don’t ask with a touch of pragmatism.

Now don’t get me wrong, I like the idea of tell don’t ask it makes perfect sense, but it’s not perfect and it can stumble. If you search the Internet for examples you often find that they’re linear in nature, reflecting the first two diagrams above where A calls B, B calls C and C calls D etc. This doesn’t reflect most applications as at some point in your programs execution you’re forced to ask an object for data, and to make a decision based upon that data. The second reason tell don’t ask stumbles is that it’s so easy to fall into the trap of asking an object for data without even thinking about it. For example, take a look at the snippet below:
      AnnotationChecker checker = new AnnotationChecker(clazz);
      if (checker.isMyComponent()) {

        Object obj = clazz.newInstance();
        result.put(clazz, obj);
      }

This example, plucked from my github dependency-injection-factory sample project shows me asking an object its state and using that information to make a decision. Procedural programming strikes again...
__________________________________________
1Direct internal state: a value held in an instance variable of an object. Derived or inferred internal state: a value returned by an object that is calculated or derived from the instance variables of that object.
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.)

Tags:

Comments

Fabrizio Giudici replied on Fri, 2012/03/16 - 5:26am

 

The examples you and James have proposed so far are too borderline for a complete evaluation. Since James told this is just the beginning of a series of posts I'm waiting for the next ones. In the meantime I can answer your question " how do you know that you’re not querying the internal state of the object?".

The question is no relevant. It's obvious that the internal state of the object is still relevant, otherwise it would be useless. The point is about coupling: you're not coupled through the internal state of the object, but through the interaction that is written in specifications. It's the minimal and correct way of coupling, since you can't do less, and it will change only when specifications change. On the opposite, if you expose the status you can potentially do everything with it. This means that TDA, in general, leads to more readable code, as it documents what's happening. If you read A methods and you find getStatus(), well, who knows what's going to happen? If you read doSomething() the situation is clear.

I've used TDA a lot in some open source projects and I can share and illustrate some code - still waiting to see the next posts by James. What I can say is that, from a practical point of view, there are some cases in which TDA doesn't change much. For instance, a TDA purist could assert that when I have to "print" the content of an object I shouldn't expose a asString() method (ask), but a renderTo(StringBuilder) (tell). In this specific case I say that there's not much difference: what you need is a String in both cases, it's clear that generally speaking that string is not part of the status, but it's just a function of the status, and when I say asString() it's quite clear what I'm going to do with it.

Comment viewing options

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