Follow The Law Of Demeter
The Law of Demeter states that we should only call objects we know firsthand. Strict adherence implies we do not have more than one dot (.) operator in an expression—no chaining of calls[1].
So
var unitPrice = product.UnitPrice;
complies but
var unitPrice = lineItem.Product.UnitPrice;
violates Demeter.
How about an extreme example?
var url = student.Course.ClassRoom.Teacher.ImageUrl;
Yes, this could be problematic.
Why?
Maybe the business stakeholders realise that in a world post-COVID-19, the need for a physical classroom to hold a course should be relaxed. A classroom is not always required. As a result of this requirement change, we decide to remove Classroom
as a property on Course
.
However, as soon as we make this change, 37 instances where the code referred to Student.Course.Classroom
break with compilation errors! We have a big problem on our hands. This issue could have been avoided by not making these deep calls into class Student
and its related objects. Class Student should only know about Course
and be ignorant of Classroom
, Teacher
and ImageUrl
.
Why is breaking the Law Of Demeter undesirable? Because knowledge of the internal working of classes increases Coupling. In tightly coupled systems, when the implementation details change in one place, many code changes will need to be made in other, seemingly unrelated areas—as we saw with 37 errors in our example. The Law of Demeter limits the effect of code changes.
Demeter is also about respecting Encapsulation, one of the key principles of object-oriented programming. Each class has its responsibility; collaborating classes can request it to do things, and it ought to do them. However, it’s not best practice to go inside the class, dig around the innards, and then do the work that the class was designed for—a code smell called Feature Envy.
The Law of Demeter helps enforce encapsulation, diminishes feature envy, reduces coupling. All good stuff. Follow the Law of Demeter.
Footnotes:
[1] Chaining of calls is not always harmful. For example, when using Fluent Syntax, call chaining creates expressions that resemble natural language: e.g. quantity.Should().Be(5);
Leave a Reply
Want to join the discussion?Feel free to contribute!