The pragmatic programmers dave and andy once summed up object oriented programming in 1 sentence: Keep it DRY, keep it shy, and tell the other guy.
We all know DRY (mostly thanks to Rails now). We just discussed tell the other guy. But this time I want to touch on keep it shy.
Now keep it shy (a.k.a the law of demeter) is about coupling. An object is coupled to another object whenever it depends on it. Being dependent means sending a message to another object. So there has to be some coupling in a system or nothing would ever happen i.e. each object would stand on its own. However, having too much coupling results in a system were you make 1 change and you break all sorts of other things that were dependent on it. Objects were designed to hide implementation and avoid situations like that. But you still have to be disciplined in keeping your objects implementation hidden and not giving it out to any object that asks for it. And at the same time, not asking an object for too much of its stuff (i.e. implementation).
For example:
class Event < ActiveRecord::Base
belongs_to :address
end
class Address < ActiveRecord::Base
has_many :events
end
Now somewhere in a view we have the following code:
<%= @event.address.street %>
Now that view is coupled to the Event
class’ implementation, specifically its
dependent on the fact that Event#address
returns an object that understands
#street
. Should we ever change that, this view would break.
If we want to keep it shy we’d have to write a wrapper method on Event
:
class Event < ActiveRecord::Base
belongs_to :address
def street
address.street
end
end
And our view becomes:
<%= @event.street %>
However, now we realize that the Address
class is un-necessary; we normalized
a little too much. So we scrap it and our schema goes from:
events (id, title, address_id)
addresses (id, street, city, state)
to:
events (id, title, street, city, state)
And our Event
class becomes:
class Event < ActiveRecord::Base
end
And our view code doesn’t need to change at all. Its still:
<%= @event.street %>
Because we wrote our view code shy, it was in no way dependent on the
implementation of how Event
stored its address information. We were able to
change the implementation of Event
and we didn’t break the view; great, now
thats what objects are all about.
Then all of a sudden, the client says they need to CRUD addresses independently, so that when creating an
Event
you can just select its address from a drop down list containing all the
addresses that are in the database. So we refactor and bring the Address
class back, moving the street
, city
and state
columns from the events
table back into the new addresses
table.
Next the client demands to see the city and state of the address in addition to its street on the web page corresponding to our view. Now that wrapper method requirement is starting to fire me up.
Because we need this:
class Event < ActiveRecord::Base
belongs_to :address
def street
address.street
end
def city
address.city
end
def state
address.state
end
end
So our view can be written nice and shy like this:
<%= @event.street %>
<%= @event.city %>
<%= @event.state %>
Now Event
is just 1 class and Address
just 1 other class its associated
with, imagine a normal app with several classes and many different associations
between them. As you can see, in order to remain shy we’re going to have a ton
of wrapper methods.
I don’t want to write all those wrapper methods, so I have to be fine with my view looking like this:
<%= @event.address.street %>
<%= @event.address.city %>
<%= @event.address.state %>
Yes it is not shy and it is dependent (coupled) to the implementation of the
Event
class but I’m going to justify that by saying views are allowed to rip
apart objects.
Ok. Hear me out here.
So the front of our app is the view i.e. the web page. And the back of our app is the database. Now the database is nothing but state - no behavior, just the results of ripping apart (flattening) an object. What about the web page? It’s always a display of that same state. So I’m saying that the web page is nothing but state and therefore has the same rights as the database in ripping apart an object in order to display it to the user.
Everything between the web page and the database is behavior. And I’d say that your classes should be shy but your views have every right not to be because they have to show the object’s state to the user.
So I’d say keep your controllers and models shy but let your views run wild!