Monday, May 14, 2007

Law of Demeter: Canary in a coal mine

I've always been a fan of the Law of Demeter. The Law of Demeter is most often summarized as “Only talk to your immediate friends.”
The fundamental notion is that a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents).
Wikipedia
That's all well and good, but I quite like the Law of Demeter because it's the canary (warning device) in my coal mine (codebase).

I was recently looking over some code with Mike Ward, Shane Harvie, Muness Alrubaie, Martin Fowler, Dan Manges, and David Vollbracht. The code in question had around a hundred lines of def_delegator :object, method_name, new_method_name. No one in the room thought that the 100+ lines of delegation were a good thing, but we all had opinions on how to deal with the ugly code.

Despite the various opinions on solutions, one thing was clear: The Law of Demeter exposed other issues within the codebase. For example, the object in question was taking on far too much responsibility. It had become the DoAnything object of the system. Additionally, methods such as diamond.clarity= were being delegated from ring via the ring.diamond_clarity= method. These methods were required as the attributes of a diamond were changed. However, a possibly better solution would be a method such as ring.replace_diamond that set an entirely new diamond. With the replace_diamond method in place the ring no longer needed to expose the setters of the diamond.

An additional note that came from this conversation: If you are a mockist you will often prefer to follow the Law of Demeter because it makes mocking much easier. However, classicists don't seem to mind as much since they can use train-wreck syntax for state based tests without any pain. Being a mockist or classicist seems to greatly influence a person's opinion on whether or not to follow the Law of Demeter.

6 comments:

  1. I hope you'll forgive me, but I've never been clear on how, exactly, the Law of Demeter helps prevent the situation outlined above. It seems to be that the Law stipulates that code of the following sort:

    some_object.items.add item

    should be replaced with:

    some_object.add_item item

    Do I misunderstand? And if that's true, it would seem to encourage the SuperObject anti-pattern for any object with non-trivial dependencies, because splitting up such an object requires callers dependent on the functionality to "reach through" to that object's new neighbours. I'd love to see a post that expands upon this in greater detail.

    ReplyDelete
  2. Instead of “Only talk to your immediate friends,” how about "If you really need to talk to someone, get them to be your friend?"

    ReplyDelete
  3. Anonymous7:36 AM

    Brian,

    I don't believe that you misunderstood. The example you used is the most popular, and I believe that's at least partly because it's the easiest to fix.

    However, any time I have a Law of Demeter violation I like to look for an alternative. This often leads a better object model or a more intent describing method call. The delegation may still occur, but in it's new form the code is more expressive about what is actually happening.

    I'll try to follow up with a few posts containing examples.

    Cheers, Jay

    ReplyDelete
  4. The way I like to think of the Law of Demeter is "Don't ask, tell." For example, instead of asking your neighbor if you can borrow his lawnmower:

    neighbor.get_lawnmover.mow(my_lawn)

    ...you should just tell him to do it for you:

    neighbor.mow(my_lawn)

    ...because it's his stupid lawnmower. Now you only need to know that your neighbor knows how to mow a lawn, not how to use his (or any) lawnmower. Plus, your neighbor can upgrade to a nice riding mower and you get to reap all the benefits!

    ReplyDelete
  5. Anonymous1:21 AM

    It seems like the LoD would generated classes whose interfaces are gigantic and totally non-cohesive. If a class has to have methods for handling all the methods of the objects it contains and recursively all the objects they contain, it seems like you would have a serious violation of the Single Responsibility Principle. How do people avoid this? Is it just that you don't typically have to expose many of your "inside" object's interfaces?

    And I'm not sure I agree with the lawn mower example given. What if the idea is that you are borrowing the mower from your neighbor to mow the lawn yourself? That's very different than telling him to mow your lawn.

    ReplyDelete
  6. A DRY way to apply Law of Demeter with demeter gem

    http://github.com/emerleite/demeter
    http://gemcutter.org/gems/demeter

    ReplyDelete

Note: Only a member of this blog may post a comment.