The Law of Demeter Design Pattern and How to Use it in Rails with the Delegate Method
The Law of Demeter, or also known as the principle of Least Knowledge, is a very interesting design pattern that you should use in your Rails projects. It enforces better encapsulation of your models and better defines its methods. The idea is that when a specific model needs information that refers to another model, it should delegate that request to the original class.
Example Before Using The Law of Demeter
Let’s say that we have two models. One being the City model and the other being the WoodProduction model. The City model simply refers to different cities and the WoodProduction model refers to the wood production levels that each of these cities has. Therefore, a “city has_one wood_production” and “wood_production belongs_to city”. So far so good. Now, we need to have a method that gets the produced wood a city, as stored in the WoodProduction model. The attribute in the WoodProduction model that refers to the wood production level is called amount. Thus, we open up our city model and create a method named wood_amount like :
def wood self.wood_production.amount end
While this works fine, there is a problem here. We are indirectly referring to another model through our City model. That is generally a bad programming idea, since it now couples our model tighter with the WoodProduction model and retrieves ‘knowledge’ that belongs to the WoodProduction class. Directly getting the wood production without the above method would be like :
Imagine that you use that inside your views a lot and suddenly, you need to distinguish wood_production.amount to raw_wood and produced_paper_amount. It becomes a big hassle now. You would need to change your views and logic from top to bottom. Let’s now see a better way of doing things.
Refactoring to Use the Law of Demeter
The Law of Demeter in Rails is summed to using just a single and not more than one ‘.’. That is, instead of using ‘city.wood_production.amount’, use ‘city.wood_production_amount’. This is done by delegating the WoodProduction amount to WoodProduction itself. This is done using the delegate method :
delegate :amount, :to => :wood_production, :prefix => true, :allow_nil => true
What this does is that it now creates an accessor(a reader) named wood_production_amount for you to use in your City model and refers directly to wood_production.amount. The :prefix=>true part is a parameter that instructs the delegate method to use wood_production as the prefix and create the delegator with the name wood_production_amount. You can change that to use whatever you want if you do want that. The :allow_nil parameter specifies that a nil is returned instead of an Exception, when the delegate object is nil. After creating this delegator, you can now use it like :
in your views or controllers, since wood_production_amount is a city instance method that delegates responsibility to the WoodProduction model, entering its ‘amount’ attribute.