The Law of Demeter is a design principle used to develop well-encapsulated software. To hide implementation details between classes, the Law of Demeter suggests the following guidelines (taken from the linked Wikipedia article):
• Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.
• Each unit should only talk to its friends; don’t talk to strangers.
• Only talk to your immediate friends.
For example, consider following classes A
, B
, and C
:
If we wanted to retrieve the string 'foo'
from an instance of A
, the following code snippet would break the Law of Demeter:
a = A.new
bar = a.b.c.foo
By accessing instances of B
and C
via a
, we’re exposing internal implementation details of the class A
, and so this program has violated the Law of Demeter. A more encapsulated design defines our classes1 like so:
With our new design, we can retrieve 'foo'
from an instance of A
like so:
a = A.new
bar = a.foo
The above code snippet can retrieve the string 'foo'
from an instance of A
without knowing the internal implementation details of A
(namely, that it refers to B
and C
when retrieving that string).
The Law of Demeter is a valuable design principle, but for simplicity and conciseness, it’s sometimes stated as “use only one dot.” If we see multiple dots when reading a value, it suggests that we’re accessing some object’s internal implementation details. a.b.c.foo
has three dots (ie. bad encapsulation), while a.foo
only has one (ie. good encapsulation), so the heuristic works for the above example.
However, dot counting is just a heuristic, and one that we can’t apply blindly. For example, what if we called the following code on an instance of C
from the previous code snippet?
c = C.new
bar = c.foo.upcase.reverse # has value of 'OOF'
c.foo.upcase.reverse
has the same number of dots as a.b.c.foo
– however, does it violate the Law of Demeter?
No, not at all. c.foo
returns a string, a built-in Ruby type. .upcase
and .reverse
are methods on that built-in type which also return strings. There’s no encapsulation that’s being broken in this example, since we’re just doing transformations on a string.
A better way to apply the “dot counting” heuristic is to:
- check if there are multiple dots when reading a value
- if it’s greater than one, check how many (non built-in) different classes are being accessed after each subsequent dot
- if that number is greater than one, there may be a violation of the Law of Demeter
Heuristics are useful, but context matters when applying them.
Start your journey towards writing better software, and watch this space for new content.
1: For the sake of clarity, I wrote a foo
method on each class in this example. However, Ruby on Rails’ delegate feature is a more concise and idiomatic way to achieve the same functionality.