Monday, December 6, 2010

Tell, don't ask. Law of Demeter

Whenever you talk to a good, experienced programmer, they will tell you that "loosely coupled" classes are very important to good software design.The Law of Demeter (LoD) or Principle of Least Knowledge is a design guideline for developing loosely coupled software, particularly object-oriented programs.

Law of Demeter for functions requires that a method M of an object O may only invoke the methods of the following kinds of objects:
  1. O itself
  2. M's parameters
  3. any objects created/instantiated within M
  4. O's direct component objects
  5. a global variable, accessible by O, in the scope of M



Example

The following Java coded illustrated this law:

public class LawOfDemeterInJava
{
  private Topping cheeseTopping;
  
  /**
   * Good examples of following the Law of Demeter.
   */
  public void goodExamples(Pizza pizza)
  {
    Foo foo = new Foo();
    
    // (1) it's okay to call our own methods
    doSomething();
    
    // (2) it's okay to call methods on objects passed in to our method
    int price = pizza.getPrice();
    
    // (3) it's okay to call methods on any objects we create
    cheeseTopping = new CheeseTopping();
    float weight = cheeseTopping.getWeightUsed();
    
    // (4) any directly held component objects
    foo.doBar

  }

  public void badExamples(Pizza pizza)
  {
    // (5) Not OK. We are calling coock() on Cheese object
    cheeseTopping.getCheese().cook();

    // (6) Not OK
    Slice slice = pizza.getSlice();
    slice.decorate()
  }

  private void doSomething()
  {
    // do something here ...
  }
}

Impact on testing.

Following this law have a huge impact on your code testability. When It comes to unit tests you faces the problem of instantiating of dependencies on class under test (CUT). Whether you choose mocks or stubs (to be consistent with terminology refer to Wikipedia or other developers source)to test you CUT, following Demeter laws significantly decrease amount of instantiation code you need to write for your tests.

6 comments:

  1. What's wrong with writing this?

    orderLine.getGroup().getOrder().getCustomer().getAddress().getPostalCode()

    ReplyDelete
  2. 1) Hard to troubleshoot NullPointerException
    2) Hard to write unit test for it.
    3) Not easy to debug

    Unless it's chaining programming style code, eg. each method invocation return instance of same object...

    ReplyDelete
  3. Hey, thanks for such a good summary and putting things together.

    I have noticed another thing in large projects. Your domain-specific object base grows, if you build dependency tree according to OOD & OOP patterns applied to these objects, you end up with a huge number of separate objects with "random" dependencies. Of course, they are not that random, but architecture starts having many shapes and it becomes difficult to maintain.

    So one of solutions is to introduce service layer holding algorithms and functionality wrapped up into "managers" like Session.XYZObjectManager, Session.KLMObjectManager...

    ReplyDelete
  4. Hey,
    No problem, I just want to break my stereotypes again and move forward in TDD and all the stuff.
    Definitely, facades eg. managers are really important party of any big application, especially frameworks.
    I've often experienced functionality hell in outsourcing projects: tens or even hundred of services, without any entry point.

    ReplyDelete
  5. Is there an error in example #6 or it is general "not OK" case that even will not allow to compile code? =)

    ReplyDelete
  6. Rrrichi, thank you for the correction, I made a mistake in example.
    It's "not OK" example because we violate Demeter Law calling method on 'child' object and we pass pizza as a parameter:

    Slice slice = pizza.getSlice();
    slice.decorate();

    ReplyDelete