Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Talking to strangers causes train wrecks


Published on

I gave this as a lightning talk at Boston.rb on 7/14//2015. It's a discussion of the challenges of applying the Law of Demeter to Rails programming.

I gave an older, simpler version of this talk at Philly.rb on 1/15/2013. I've replaced those older slides.

Published in: Internet
  • Be the first to comment

  • Be the first to like this

Talking to strangers causes train wrecks

  1. 1. July 14, 2015 Talking to strangers causes train wrecks Mike Toppa CTO, Poka Yoke Design @mtoppa
  2. 2. My house, yesterday I just moved to Boston from Philly. I took this picture yesterday. It looks pretty much the same today.
  3. 3. Goal: unpack this quote “Mockist testers do talk more about avoiding 'train wrecks' - method chains of style getThis().getThat().getTheOther(). Avoiding method chains is also known as following the Law of Demeter. While method chains are a smell, the opposite problem of middle men objects bloated with forwarding methods is also a smell. (I've always felt I'd be more comfortable with the Law of Demeter if it were called the Suggestion of Demeter.)” Martin Fowler, 2004 Mocks aren’t Stubs
  4. 4. The Law of Demeter, I ✤ Your method can call other methods in its class directly def full_name "#{self.first_name} #{self.last_name}" end ✤ You can play with yourself From "Law of Demeter" Back in the 1980s, a group of programmers working on a project called the Demeter system realized that certain qualities in their object-oriented code led to the code being easier to maintain and change. [from Avdi Grimm] Analogy by Peter VanRooijen
  5. 5. The Law of Demeter, II ✤ Your method can call methods on its own fields directly (but not on the fields' fields) ✤ You can play with your own toys (but you can’t take them apart) From "Law of Demeter"
  6. 6. The Law of Demeter, III ✤ When your method takes parameters, your method can call methods on those parameters directly def user_info(user) "Name: #{user.full_name}" end ✤ You can play with toys that were given to you From "Law of Demeter"
  7. 7. The Law of Demeter, IV ✤ When your method creates local objects, that method can call methods on the local objects # Sorry, I can’t give an example without # going on a tangent about dependency injection ✤ And you can play with toys you’ve made yourself From "Law of Demeter"
  8. 8. Violation example in a Rails project In Ruby, and Rails, this kind of chaining is so easy to do, you might not realize the implications of what you’re doing. There are 3 concerns to emphasize: 1. This code is brittle. If any component of the method chain changes, this line of code, and any others like it, will break. Coupling is high, and information is not localized. 2. In the context of ActiveRecord, this approach has the potential to yield horrific, poorly performing underlying SQL queries. 3. If the code was in a method, it would be challenging to write a unit test for it
  9. 9. Testing with train wrecks def user_info(user) "Name: #{}. " "Boss: #{user.department.try(:head).try(:name)}" end From Demeter: It’s not just a good idea. It’s the law. This example is from Avdi Grimm
  10. 10. describe '#user_info' do subject { user_info(user) } let(:user) { stub( 'user', name: "Bob", department: stub( name: "Accounting", head: stub( name: "Jack", position: stub(title: "Vice President") ) ), division: stub( name: "Microwave Oven Programming" ) ), position: stub(title: "Senior Bean Counter") ) } # examples... end From Demeter: It’s not just a good idea. It’s the law. Alternately, you could try to do this without mocks, and use factories or fixtures instead. But that just moves the brittleness into your test data setup.
  11. 11. Reducing Rails train wrecks: Delegation From Demeter: It’s not just a good idea. It’s the law. class User delegate :name, to: :department, prefix: true, allow_nil: true end … def user_info(user) "Name: #{}. Dept: #{user.department_name}" end While Demeter allows us to play with our friends, delegation can still help reduce brittleness. If there is a change in the relationship between users and department, we can create a department_name method that expresses the new logic, without having to change calling code. For example, if users can now be part of more than one department.
  12. 12. Reducing Rails train wrecks: has many :through class User < ActiveRecord::Base has_many :departments has_many :divisions, through: :departments end Now we can treat divisions as an immediate collaborator. You can say this is cheating, but the point is, information about divisions is now localized. If something changes in the relationship to divisions, we can replace the “has many through” relationship with a “divisions” method.
  13. 13. Reducing Rails train wrecks: nested has many :through class Candidate < ActiveRecord::Base has_many :campaigns has_many :races, through: :campaigns has_many :sought_offices, through: :races end
  14. 14. Fowler’s conundrum: Demeter as suggestion, or law? ✤ With nested has many :through, now we can say: ✤ candidate.sought_offices ✤ With Rails, we don’t need “middle men objects bloated with forwarding methods” to access distant collaborators ✤ The question I’ll leave you to ponder is: ✤ Is this a good solution to Fowler’s conundrum?
  15. 15. ポカヨケ @mtoppa My wife has a new job in Boston, so we’re moving, and I’ll be starting a new job too. I’ll be co-founder and CTO of Poka Yoke Design, and I’m looking forward to working more with WordPress again. My partner is based in Memphis, so I’m very pleased to be strengthening my ties to the WordPress community here in Tennessee.