Discovering new ways to improve our code is something that all developers strive for. Whether that’s reducing the amount of lines in our code, narrowing down redundant logic, having helper methods rather than“one-method-to-rule-them-all”, or simply improving performance, refactoring code can be a rewarding experience for strengthening your application and your own knowledge.
Enter the Law of Demeter.
The Law of Demeter is a set of design principles for Object-Oriented-Programming (OOP), created by Ian Holland at Northeastern University. Per wikipedia, the major tenants state:
1. Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.
2. Each unit should only talk to its friends; don’t talk to strangers.
3. Only talk to your immediate friends.
In terms of Ruby on Rails applications, we often refer to this as the “one-dot” rule (more on this later). The key takeaway is this — when working with an application that leverages multiple models and the relationships between them, your object instances should really only have knowledge of their immediate fellows.
For instance, in a Game of Thrones (ok, A song of Ice and Fire) compendium, let’s say we have a model for a House, such as Lannister. A house can have many knights associated with it through a 1:N relationship, since a knight belongs to a single house, but can also have many sellswords working for it as well.
Because we know sellswords can be pretty opportunistic, let’s say a certain sellsword can work for multiple houses at once. We can then use a Contract join model to handle this N:N relationship.
What does this mean for how we actually write our code though?
In a basic Rails application, if you’re chaining together methods on your view pages (or wherever your users are interfacing with your application), you want to avoid chaining too many methods. This is for a couple of reasons:
- Responsibility: views should be in charge of returning info to users, not incorporating the logic to get said info.
- Protection: ensures that changes to various model logic is encapsulated in the model itself, where it has a smaller chance of impacting the end-user experience.
The rule of thumb is that if you find you are chaining together more than one method in your view, then you’re violating the Law of Demeter and should look to try and pare that down to one method (Hence, the “one-dot” rule I mentioned earlier).
For instance, if we wanted to display the number of houses a sellsword is currently serving, we could chain together a couple of methods in our view like this:
As you can see, we’ve got three methods chained to our instance variable , thus violating the Law of Demeter — it should not be our view’s responsibility to implement the logic to pull this information, but rather the model itself to perform the logic and our view to just displays the results of said logic.
How can we improve this?
Let’s refactor this code by adding a #contracts_count instance method to our Sellsword model:
We can then change our view method to be more in line with the Law:
Another example — What if our knight page has a section to display the knight’s house info? We could do something like the following:
Once again however, we are violating the Law of Demeter. In this case, it doesn’t really make sense to make an instance method for each of these. Doing so would clutter our code and methods should generally be reserved for more complex logic, like our count from above. Enter the #delegate method! The delegate method is one of those “magical” Rails methods that allows us to be dedicated adherents to the Law of Demeter.
Using the delegate method, we can add the following code to our Knight model:
which changes our view code to instead be:
(Note that I used @knight.house_name instead of @knight.name because both knight’s and houses have names)
Great! Now our code has been reformatted to follow the Law of Demeter, thus making it less prone to future mistakes and, dare I say, a bit cleaner to read as well.
Following the Law can be hard, but as shown above, utilizing helper methods and the Rails #delegate module are just a few ways we can improve our code so we don’t end up in developer’s court…I’ll see myself out.