The Law of Demeter, The crux of

June 21, 2009 20:12
var zipCode = order.Customer.Address.ZipCode;

Have something like that in your code? Suprise, surpsire - this is called "train wreck" coding style. To get the zip, we should know (and rely on) the whole chain of components. An unrelated change somewhere else in the system can break the line above.

To get rid of such chains, we should recall The Law of Demeter and, generally, the notion of "shy" code. Shy is two-fold:
  • don't reveal too much of yourself to others. Do not mark things public if they can be private (or internal, etc), avoid putting several methods of a similar purpose on your public API, avoid global data (and avoid singletons - if feasible - because they are global).   
  • don't interact with others more than it's necessary. Basically, call as few methods as possible. Methods with smaller response set (response set is a number of functions/properties called by a method) are less prone to error and break less frequently, as they're less coupled with the rest of the system. 


Hiding the List<T>.

Usually people look at this and say it's crazy, we cannot proxy everything! If we expose a generic list of monkeys there's no way we would put MonkeyCount property instead of using monkeys.Count.

That's correct - List<T> will never give up its Count property. However, exposing List<T> means changing it to something else would be a job of work after it is accessed in 70 places all over the codebase and some methods grab your list and modify it directly, and then you'll have to rewrite 'em all to get their dirty paws off your data.

Next, if you'd need anything else in
monkeys.Add(monkey) - validation, eh? - anyway you'd have to eventually hide the list and use AddMonkey and RemoveMonkey instead. By starting down that route from the very beginning, you just save time (and you'll probably have to expose just a few methods of the original list: add, remove, count and removeAll should be enough in most cases).  

The Law of Demeter for functions.

The Law of Demeter for functions states that any method of an object should call only methods (and access only properties) belonging to:

  1. class/struct containing the method
  2. any parameters passed into the method
  3. any types explicitly created in the method
  4. any objects directly held by the containing class or struct


class Demeter
{
   private PrivateField _myField;
   public void MyMethod() { ... }
   
   public void Test(Parameter methodParameter)
   {
      ...
      MyMethod();             //(1) method from the same class
      ...
      methodParameter.Foo();  //(2) method from a parameter passed in
      ...
      var local = new Local();      
      local.Boo();            //(3) method from a local object we created
      ...
      _myField.Bar();         //(4) method from a directly held instance
   }
}

To nail down, here go a few snippets, inspired by a top notch book The Pragmatic Programmer. Tell if the code below is ok from The Law of Demeter point of view:

bool AddProduct(Product product)
{
   var supplier = product.Supplier;
   if (supplier.IsShipSupported(_myCity)) {
      _products.Add(product);
      return true;
   }
   return false;
}

The answer (select with your mouse): product is passed as a parameter, so access to Supplier property is fine. Calling IsShipSupported, however, is not: we neither create supplier nor get it as a parameter, so we cannot access it's methods and properties. To fix that, we could have created an IsShippable method on Product type.

Another example, look if
the code below is ok from The Law of Demeter point of view:

class Brain
{
   private Analyzer _analyzer = new Analyzer();
 
   public void Think(Idea idea)
   {
      if(!_analyzer.IsComplicated(idea.GetComplexity()) 
         ThinkCore(idea);      
   }
}

The answer (select with your mouse): Analyzer is owned by Brain class so access to IsComplicated method is fine; idea is passed as a parameter so calling GetComplexity is ok as well.

The optional part of the law.

Let's look again on that snippet from the very beginning of the post.

var zipCode = order.Customer.Address.ZipCode;

Instead of digging through the hierarchy, you can add a property ZipCode on Order type, which in turn would simply delegate the call to ZipCode property of Customer, which in turn would call ZipCode of Address.

It is easy to get into the trap and start adding all those wrappers - and if you do, your classes can become a mess with no cohesive API. And, well, okay, m
ethod chaining is a code smell, but the types bloated with forwarding methods is also a smell, correct?

The Law of Demeter is not about egalitarian wrapping of method calls, and that's why an automated refactoring tool would not help you. As mentioned above, if you shield a List<T> you'd probably be ok with exposing add, remove,  clearall and count: no need to expose everything. However, I wouldn't recommend creating zip code proxies for the types in the original example.

I
t is all about deciding when to tell a method to do something vs. when to ask it for data and do the job yourself.

Where to go from here.

Usually, a peer review would help to identify if a violation is acceptable, or a proxy method is needed, or a completely different solution would work out (say, the VisitorPattern: if you have to do something to an object X at the end of a long chain, have a Visitor look for X with the collaboration of all involved items).

So let me put it this way: you can violate the Law of Demeter but there should be a reason for that. I still believe the Law of Demeter for functions provides a powerful and easy way to spot design problems, something you cannot just shrug off. When it comes down to properties, it gets different, because of that "tell vs. ask" dilemma.

To build your own opinion, read on:

 

Comments are closed