Demeter's law (suggestion) and tell don't ask - Part 2

Part 1 suggested that Tell-Don't-Ask might be a better principle to keep in mind than the Law of Demeter. There is a common problem while trying to code tell-don't-ask. The object-to-be-told may not be available at the point of making the call. For example, when a book is returned, you may want to notify another user (if any) waiting for the title. For the purpose of this example, the domain model is: every physical book (copy) is associated with a Title. Users lookup titles and checkout books. The Title knows how many copies are in stock and how many of those are lent out. To start off, here is a fairly bad implementation:


returned(){ //a method on Book
setStatus(Book.AVAILABLE);
title.setNumberOfCopiesAvailable(title.getNumberOfCopiesAvailable + 1);
title.getUserInWait().notifyAvailability(title);
}

The Book asks the Title two things and tells very little. One could imagine the following conversation:

Book: How many copies are available? And who's waiting for you?
Title: Why do you ask?
Book: I was just returned to the library. I want to make sure the number of copies available reflect this and also notify whoever is waiting.
Title: Why don't you just tell me that you are back. I'll take care of the housekeeping myself.

This is a slightly contrived example, but to illustrate the point, let's start off by removing the 'ask' from the last line. By introducing a forwarding wrapper in Title, book can just tell Title to notify any waiting user. There is good support for such delegation in IntelliJ/Eclipse.


Right-Click -> Delegateright click select delegate method
Select target object userInWaitchoose target object userInWait
Select target method notifyAvailabilitychoose target method notifyAvailability
Donefinal result

So we now have:


returned(){
setStatus(Book.AVAILABLE);
title.setNumberOfCopiesAvailable(title.getNumberOfCopiesAvailable + 1);
title.notifyAvailability();
}

Now, we can push the logic of updating the number of available copies into title.notifyAvailability and rename it to title.notifyReturn


returned(){
setStatus(Book.AVAILABLE);
title.notifyReturn();
}

What started off as dumb forwarding (title.notifyAvailability -> user.notifyAvailability) has thus ended up as better encapsulation (title.notifyReturn).

3 comments:

Saager Mhatre said...

Possibly a stupid sideline, but interesting nonetheless. There's a lot of ways of looking at this.

* Delegate, don't inherit
* Chain of Observers- User observes Title, Title observes Book
* Chain of responsibility without the common handler type

BTW, I'm glad we saw more objects interacting this time.

Saager Mhatre said...

Was reading one on Martin Fowler's old articles and landed this. Fowler also talks of delegating-middle-men objects, but I guess you addressed that in the last part of your article. I have, nevertheless, seen (and been part of) code bases that end up with middle men just to satisfy LoD (and have hated them to no end).

Sriram said...

@Saager

Yes, I think what start off as dumb forwarding methods usually end up gaining useful behaviour. Forwarding methods suck less than having behaviour where it doesn't belong.

Post a Comment