talking to strangers causes train wrecks
Post on 13-Jan-2015
1.209 Views
Preview:
DESCRIPTION
TRANSCRIPT
January 15, 2013
Talking to strangers causes train wrecksMike Toppa, ElectNext.com @mtoppa
Wednesday, January 16, 13
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, 2004Mocks aren’t Stubs
Wednesday, January 16, 13
✤ What is the Law of Demeter?
✤ How does it pertain to traditional OO vs. ActiveRecord?
✤ What are the implications for testing?
✤ What are the alternatives to train wrecks?
Steps to understanding
Wednesday, January 16, 13
The Law of Demeter
✤ AKA Principle of Least Knowledge, AKA “Don’t talk to strangers”
✤ Only Talk To Friends
✤ Objects Closely Related To The Class
✤ Reduces Dependencies
✤ Benefits:
✤ Maintainable Code
✤ Reusable Code
✤ Easy To Understand CodeFrom Don’t Be STUPID, grasp SOLID
Wednesday, January 16, 13
A helpful analogy....
✤ You can play with yourself.
✤ You can play with your own toys (but you can’t take them apart),
✤ You can play with toys that were given to you.
✤ And you can play with toys you’ve made yourself.
From Peter Van Rooijen, WikiWiki
Wednesday, January 16, 13
Traditional OO approach: dependency inversion
<<interface>>
SwitchableDevice+turnOn()+turnOff()
Button+ poll()
Lamp
class Button { private $switchableDevice;
public function __construct(SwitchableDevice $switchableDevice) { $this->switchableDevice = $switchableDevice; }
public function poll() { if (/* some condition */) { $this->switchableDevice->turnOn(); }} From Agile Software Development
Wednesday, January 16, 13
Typical Rails approach:Method chaining
✤ ActiveRecord makes it easy, but at the cost of lost abstraction:
✤ Button class: has_one :lamp
✤ Lamp class: belongs_to :button
✤ button.lamp.turn_on
✤ Which makes it easy to end up with a train wreck...
✤ person.timers.first.button.lamp.turn_on
Wednesday, January 16, 13
✤ Mocks become impractical
✤ Factories may help, but FactoryGirl can only go two levels deep:
@weed = FactoryGirl.create(:politician, :with_campaign, name: 'M. Teresa Paiva-Weed')
✤ You will probably need to rely on fixtures or seeding with test data
✤ Bottom line: you will have to rely on integration testing, not unit testing
Testing with train wrecks
Wednesday, January 16, 13
Reducing the number of dots I
Use a has_many :through relationship
class Physician < ActiveRecord::Base has_many :appointments has_many :patients, :through => :appointmentsend
Wednesday, January 16, 13
Reducing the number of dots II
Use delegation
class User delegate :name, :to => :department, :prefix => true, :allow_nil => true # ...end
....
def user_info(user) "Name: #{user.name}. Dept: #{user.department_name}"end
From Demeter: It’s not just a good idea. It’s the law.
Wednesday, January 16, 13
Reducing the number of dots III
✤ Use POROs (plain old ruby objects) for business logic. This means
✤ Your Rails models will be skinny
✤ You can use whatever principles and patterns you want
✤ For me, this is where the conundrum lies:
✤ The speed and power of “the Rails way”
vs.
✤ The adaptability (and testability) of traditional OO patterns
Wednesday, January 16, 13
top related