The Law of Demeter

Recently I re-read The Pragmatic Programmer mostly while on a break in Mexico. I also did my fair share of sitting in the sun, eating and event drinking but I read a LOT as well.

And I keep learning, or at least reminding myself, of several things I think are really useful to all of us.

In terms of writing OOP code the Law Of Demeter is one that is really useful. Basically in this Law you should only access :

– items (properties, methods, constants, etc) of the of the object you are part of
– items (properties, methods, constants, etc) you are passed as parameters
– items (properties, methods, constants, etc) of objects you create in your code

This limits the knowledge your code needs of the internals of other objects.

Suppose we have a set up something like :

Global Dim someGlobalVar as integer

Class FooBar
   Dim i as integer
   Sub fubaz() as string
   End Sub
End Class

Class Bar
   Dim fubar as FooBar
   Sub Constructor()
     fubar = new FooBar
   End Sub 
 
   Sub Baz()
   End Sub
End Class

Class Foo
  Dim BarInstance as Bar

  Sub Constructor
    self.BarInstance = new Bar
  End Sub

  Function HasInstance() as Boolean
    // this one is OK !!!!!
    return (BarInstance is nil) = false   
  End Function

  Sub CallBazOfBar()
    // this one is OK !!!!!
    BarInstance.Baz()
  End function

  Function CallBazOfPassedInBar(b as Bar)
    // this one is OK !!!!!
    b.Baz()
  End Function

  Function CreateAndCallBazOfBar()
    // this one is OK !!!!!
    dim b as new Bar
    b.Baz()
  End Function

  Function ReachesThroughBarToFooBar() as integer
    return BarInstance.fubar.i
  End function

  Function ReliesOnGlobal() as integer
    return someGlobalVar
  End function

  
End Class 

Then for us to adhere to this principle the following new methods on our Class Foo would NOT be ok :

Function ReachesThroughBarToFooBar() as integer
   return BarInstance.fubar.i
End function

This one is NOT ok because it requires the Foo class to know more about the internals and implementation of the Bar class AND FooBar class than it should. Instead the Bar class should have a method on it that can be invoked to get details of its properties or classes it owns.

Function ReliesOnGlobal() as integer
   return someGlobalVar
End function

This one is NOT ok because it requires the Foo class to rely on a global variable that is outside the scope of the instance and the method using it.

Methods in our Foo class should ONLY use :

  1. methods in the instance itself (HasInstance is OK)
  2. methods of any parameters passed to the methods like CallBazOfPassedInBar(b as Bar)
  3. Any objects created/instantiated within the method like CreateAndCallBazOfBar
  4. methods in the instances properties (in this case methods of Bar like CallBazOfBar)
  5. A global variable, accessible by the instance, that is in the scope of the method (this last one is harder to explain)

I like the Wikipedia summary which is

 For many modern object oriented languages that use a dot as field identifier, the law can be stated simply as “use only one dot”. That is, the code a.b.Method() breaks the law where a.Method() does not.

https://en.wikipedia.org/wiki/Law_of_Demeter