I read multiple things about the Law of Demeter but I never truly understood what it really means until I read Sandi Metz's book Practical Object-Oriented Design in Ruby (POODR). This article is shamelessly inspired from her work smile

Definition

It's quite hard to understand the Law of Demeter when encountered for the first time. As seen in the definition from Wikipedia :

More formally, the Law of Demeter for functions requires that a method of an object may only invoke the methods of the following kinds of objects :

  1. object itself
  2. method's parameters
  3. Any objects created/instantiated within method
  4. object's direct component objects
  5. A global variable, accessible by object, in the scope of method

More generally, Law of Demeter is a range of coding rules that helps keep our objects loosely coupled. We always refer it as the "use only one dot" rule or "only talk to your immediate neighbors".

But what does it really mean ?

What Demeter is telling us

Consider we have a Rental class that contains a depart method. And we have an Owner model, that own a Car. A Car has an Engine which contains an oil_level attribute and a start method. Let's say we have implemented our depart method like this :

class Rental
  def depart
    if owner.car.engine.oil_level > 10
      owner.car.engine.start
    else
      puts "Oil level too low"
    end
  end
end

The chaining dots in lines 4 and 5 are almost identical. The first one retrieves a distant attribute (oil_level) and the second one invokes a distant behavior (start).

What's wrong with those lines ?

It's a clear Demeter violation. Let's list our issues with this piece of code :

  • If we change something in Engine, it might break something in Rental, which is completely unrelated. This code is not transparent.
  • Rental cannot be reused unless it has access to an owner that has a car which contains an engine that responds to start.

This design increases the coupling of our application, and coupling is bad, really bad. You know that moment when you change something in class A and suddenly, it breaks another thing in class B ? Well, most of the time it's due to the fact that your application is too much coupled.

Now think about it. In our example, Rental knows that an owner has a car with an engine and it also knows that this engine can be started. It knows way too much.

So, what can we do about that ?

Object-Oriented Design is about managing dependencies. — Sandi Metz

Note : in owner.car.engine.oil_level we try to access a distant attribute. It is also a Demeter violation but it is a particular case because it might be cheaper not to change this. In some cases, if we just need to access the attribute, we can keep this as it is. Just remember that this tradeoff is permitted as long as we are not changing the value of the attribute we retrieve. If we change the value of the attribute, we are implementing a new behavior that belongs to Engine, notRental`.

Focus on behavior rather than data

What if we can do something like that :

class Rental
  def depart
    owner.drive
  end
end

Wouldn't be amazing ?

Well, guess what ? We can.

And by doing so, we are trusting other objects to behave according to the message we are sending them. As Rental, we don't want to know how the owner plans to drive and start his car. We just need to trust him that he will.

Then, we can implement the drive method in our Owner class and delegate the start part to the last object that needs to know about it : Engine. We end up with this :

class Rental
  def depart
    owner.drive
  end
end

class Owner
  def drive
    car.switch_on
  end
end

class Car
  def switch_on
    engine.start
  end
end

class Engine
  def start
    if oil_level > 10
      # start the engine
    else
      puts "Oil level too low"
    end
  end
end

Those small modifications increase significatively the quality of our code. Each object trusts each other that it will do its job.

Rental is now "open for extension and closed for modification".

What does it mean exactly ? I think it deserves a post on its own.

#object-oriented #design #ood #design #ruby #lod