July 14, 2015
Talking to strangers causes train wrecks
Mike Toppa
CTO, Poka Yoke Design
@mtoppa
http://pokayoke.design
My house, yesterday
I just moved to Boston from Philly. I took this picture yesterday. It looks pretty much the same today.
Goal: unpack this quote
“Mockist testers do talk more about avoiding 'train wrecks' - method
chains of style getThis().get...
The Law of Demeter, I
✤ Your method can call other methods in its class directly
def full_name
"#{self.first_name} #{self....
The Law of Demeter, II
✤ Your method can call methods on its own fields directly (but not on
the fields' fields)
user.departm...
The Law of Demeter, III
✤ When your method takes parameters, your method can call methods
on those parameters directly
def...
The Law of Demeter, IV
✤ When your method creates local objects, that method can call
methods on the local objects
# Sorry...
Violation example in a Rails project
In Ruby, and Rails, this kind of chaining is so easy to do, you might not realize the...
Testing with train wrecks
def user_info(user)
"Name: #{user.name}. "
"Boss: #{user.department.try(:head).try(:name)}"
end
...
describe '#user_info' do
subject { user_info(user) }
let(:user) {
stub(
'user',
name: "Bob",
department:
stub(
name: "Acco...
Reducing Rails train wrecks:
Delegation
From Demeter: It’s not just a good idea. It’s the law.
class User
delegate :name, ...
Reducing Rails train wrecks:
has many :through
class User < ActiveRecord::Base
has_many :departments
has_many :divisions, ...
Reducing Rails train wrecks:
nested has many :through
class Candidate < ActiveRecord::Base
has_many :campaigns
has_many :r...
Fowler’s conundrum:
Demeter as suggestion, or law?
✤ With nested has many :through, now we can say:
✤ candidate.sought_off...
ポカヨケ
http://pokayoke.design
@mtoppa
My wife has a new job in Boston, so we’re moving, and I’ll be starting a new job too. ...
Upcoming SlideShare
Loading in …5
×

Talking to strangers causes train wrecks

1,550
-1

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
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,550
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Talking to strangers causes train wrecks

  1. 1. July 14, 2015 Talking to strangers causes train wrecks Mike Toppa CTO, Poka Yoke Design @mtoppa http://pokayoke.design
  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 c2.com "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) user.department.name ✤ You can play with your own toys (but you can’t take them apart) From c2.com "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 c2.com "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 c2.com "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: #{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: #{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. ポカヨケ http://pokayoke.design @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.
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×