Top Banner
John Cinnamond // panagile ltd Strong Duck Type Driven Development
192

Strong Duck Type Driven Development

Jul 19, 2015

Download

Software

jcinnamond
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Strong Duck Type Driven Development

John Cinnamond // panagile ltd

Strong Duck Type Driven Development

Page 2: Strong Duck Type Driven Development
Page 3: Strong Duck Type Driven Development
Page 4: Strong Duck Type Driven Development

strong duck type driven development

Page 5: Strong Duck Type Driven Development

Let’s build a duck

Page 6: Strong Duck Type Driven Development

How do you build a duck?

Page 7: Strong Duck Type Driven Development

What is a duck?

Page 8: Strong Duck Type Driven Development

Let’s deconstruct a duck

Page 9: Strong Duck Type Driven Development

quacky bit

walking bit

floaty bit

flying bit

Page 10: Strong Duck Type Driven Development

quacky bitwalking bitfloaty bitflying bit

Page 11: Strong Duck Type Driven Development

This is a pretty rubbish duck

too much negative space

Page 12: Strong Duck Type Driven Development
Page 13: Strong Duck Type Driven Development

This is still a pretty rubbish duck

The bits don’t fittogether very well

Page 14: Strong Duck Type Driven Development

floaty bit

Page 15: Strong Duck Type Driven Development
Page 16: Strong Duck Type Driven Development

This is still a pretty rubbish duck

Page 17: Strong Duck Type Driven Development

quacky bit

walking bit

floaty bit

flying bit

Page 18: Strong Duck Type Driven Development

quacky bit

Page 19: Strong Duck Type Driven Development

this is worse

Page 20: Strong Duck Type Driven Development

floaty bit

Page 21: Strong Duck Type Driven Development

something isn’t quite right

Page 22: Strong Duck Type Driven Development

I changed the quacky bit

Page 23: Strong Duck Type Driven Development

I was required to change the floaty bit too

Page 24: Strong Duck Type Driven Development
Page 25: Strong Duck Type Driven Development

quacky bit

neck bit

brain bit eye bit

Page 26: Strong Duck Type Driven Development
Page 27: Strong Duck Type Driven Development

floaty bit

this is going to

hurt

Page 28: Strong Duck Type Driven Development

floaty bit

Page 29: Strong Duck Type Driven Development

We focus on the bits we’re building

Page 30: Strong Duck Type Driven Development

We don’t think about how the bits fit together

Page 31: Strong Duck Type Driven Development

this causes

problems

Page 32: Strong Duck Type Driven Development

John Cinnamond // panagile ltd

Strong Duck Type Driven Development

Page 33: Strong Duck Type Driven Development
Page 34: Strong Duck Type Driven Development
Page 35: Strong Duck Type Driven Development

Let’s build a duck

Page 36: Strong Duck Type Driven Development

class QuackyBit

def quack puts “quack” end

end

Page 37: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

end

Page 38: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

def loud_quack puts “QUACK” end

end

Page 39: Strong Duck Type Driven Development

bundle exec rspec ..........................................................................

Finished in 43.32027 seconds 73 examples, 0 failures

Page 40: Strong Duck Type Driven Development

bundle exec rspec ..........................................................................

Finished in 43.32027 seconds 73 examples, 0 failures

Page 41: Strong Duck Type Driven Development

wat?

Page 42: Strong Duck Type Driven Development

either

the code is not used

or

the tests are wrong

Page 43: Strong Duck Type Driven Development

class DuckBrain

def say_hello if other_duck.nearby? @quacky_bit.quack end end

end

Page 44: Strong Duck Type Driven Development

class DuckBrain

def say_hello if other_duck.nearby? @quacky_bit.quack end end

end

Page 45: Strong Duck Type Driven Development

it “greets other ducks” do expect(quacky_bit).to receive(:quack)

duck_brain.say_hello end

Page 46: Strong Duck Type Driven Development

it “greets other ducks” do expect(quacky_bit).to receive(:quack)

duck_brain.say_hello end

Page 47: Strong Duck Type Driven Development

How would we write it in Go?

Page 48: Strong Duck Type Driven Development

type DuckBrain struct { quackyBit Quacker }

func (b DuckBrain) sayHello() { b.quackyBit.quack() }

Page 49: Strong Duck Type Driven Development

type DuckBrain struct { quackyBit Quacker }

func (b DuckBrain) sayHello() { b.quackyBit.quack() }

Page 50: Strong Duck Type Driven Development

type Quacker interface { quack() }

Page 51: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) quack() { fmt.Println(“quack”) }

Page 52: Strong Duck Type Driven Development

beak := Beak{}

brain := DuckBrain.new { quackyBit: beak, }

brain.sayHello()

Page 53: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) quack() { fmt.Println(“quack”) }

Page 54: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) softQuack() { fmt.Println(“quack”) }

Page 55: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) softQuack() { fmt.Println(“quack”) }

func (b Beak) loudQuack() { fmt.Println(“QUACK”) }

Page 56: Strong Duck Type Driven Development

./duck.go:4:

cannot use beak (type Beak) as type Quacker in field value:

Beak does not implement Quacker (missing quack method)

Page 57: Strong Duck Type Driven Development

type Quacker interface { softQuack() loudQuack() }

Page 58: Strong Duck Type Driven Development

./duck_brain.go:6:

db.quackyBit.quack undefined

(type Quacker has no field or method quack)

Page 59: Strong Duck Type Driven Development

type DuckBrain struct { quackyBit Quacker }

func (b DuckBrain) sayHello() { b.quackyBit.quack() }

Page 60: Strong Duck Type Driven Development

The compiler tells me when I break stuff

Page 61: Strong Duck Type Driven Development

The compiler hints about how to fix it

Page 62: Strong Duck Type Driven Development

DuckBrain knows nothing about Beak

Page 63: Strong Duck Type Driven Development

Beak knows nothing about DuckBrain

Page 64: Strong Duck Type Driven Development

Interfaces are no longer intangible

Page 65: Strong Duck Type Driven Development

Hey Jakub

Page 66: Strong Duck Type Driven Development

wouldn’t it be great if we had

interfaces in ruby?

Page 67: Strong Duck Type Driven Development

in rubythis is easy

Page 68: Strong Duck Type Driven Development

just write it

Page 69: Strong Duck Type Driven Development

word

Page 70: Strong Duck Type Driven Development

class DuckBrain

def initialize(quacky_bit) @quacky_bit = quacky_bit end

end

Page 71: Strong Duck Type Driven Development

class DuckBrain

def initialize(quacky_bit) if !quacky_bit.respond_to?(:quack) raise InterfaceError end @quacky_bit = quacky_bit end

end

Page 72: Strong Duck Type Driven Development

We just invented NoMethodError

Page 73: Strong Duck Type Driven Development

There are contracts between objects

Page 74: Strong Duck Type Driven Development

One object promises to provide some methods

Page 75: Strong Duck Type Driven Development

The other promises to only use these methods

Page 76: Strong Duck Type Driven Development

require 'lawyer'

class Quacker < Lawyer::Contract

end

Page 77: Strong Duck Type Driven Development

require 'lawyer'

class Quacker < Lawyer::Contract confirm :quack => 0 end

Page 78: Strong Duck Type Driven Development

class QuackyBit

def quack puts “quack” end

end

Page 79: Strong Duck Type Driven Development

class QuackyBit

def quack puts “quack” end

end

QuackyBit.implements(Quacker)

Page 80: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

end

QuackyBit.implements(Quacker)

Page 81: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

def loud_quack puts “QUACK” end

end

QuackyBit.implements(Quacker)

Page 82: Strong Duck Type Driven Development

QuackyBit does not implement <Quacker>

Page 83: Strong Duck Type Driven Development

QuackyBit does not implement <Quacker>

(Lawyer::BrokenContract) (1 method missing) [missing] quack

Page 84: Strong Duck Type Driven Development

class Quacker < Lawyer::Contract confirm :quack => 0 end

Page 85: Strong Duck Type Driven Development

class Quacker < Lawyer::Contract confirm :soft_quack => 0 confirm :loud_quack => 0 end

Page 86: Strong Duck Type Driven Development

it “greets other ducks” do

end

Page 87: Strong Duck Type Driven Development

it “greets other ducks” do

duck_brain.say_hello expect(<something>). to have_received(:quack) end

Page 88: Strong Duck Type Driven Development

it “greets other ducks” do brain = DuckBrain.new(quacker)

duck_brain.say_hello expect(quacker). to have_received(:quack) end

Page 89: Strong Duck Type Driven Development

it “greets other ducks” do quacker = contract_double( Quacker )

brain = DuckBrain.new(quacker)

duck_brain.say_hello expect(quacker). to have_received(:quack) end

Page 90: Strong Duck Type Driven Development

it “greets other ducks” do quacker = contract_double( Quacker )

brain = DuckBrain.new(quacker)

duck_brain.say_hello expect(quacker). to have_received(:quack) end

Page 91: Strong Duck Type Driven Development

Failure/Error: duck_brain.say_hello

Double "Quacker" received unexpected message :quack with (no args)

Page 92: Strong Duck Type Driven Development

The compiler tells me when I break stuff

toolchain

Page 93: Strong Duck Type Driven Development

This is still ruby

Page 94: Strong Duck Type Driven Development

This is just duck typing

Page 95: Strong Duck Type Driven Development

But it’s strong duck typing

Page 96: Strong Duck Type Driven Development

The toolchain helps enforce the types

Page 97: Strong Duck Type Driven Development

We check the type before runtime

Page 98: Strong Duck Type Driven Development

We have timeto fix type problems

Page 99: Strong Duck Type Driven Development

Is this actually useful?

Page 100: Strong Duck Type Driven Development

Initially I was

🙋

Page 101: Strong Duck Type Driven Development

“I have more confidence that my production code won’t break”

Page 102: Strong Duck Type Driven Development

I’ve only seen interface problems a few times…

Page 103: Strong Duck Type Driven Development

…in 8 years of writing ruby code

Page 104: Strong Duck Type Driven Development

I’ve solved the wrong problem

#fml

Page 105: Strong Duck Type Driven Development

The compiler hints about how to fix it

Page 106: Strong Duck Type Driven Development

The object don’t know about each other

Page 107: Strong Duck Type Driven Development

Interfaces are no longer intangible

Page 108: Strong Duck Type Driven Development

John Cinnamond // panagile ltd

Strong Duck Type Driven Development

Page 109: Strong Duck Type Driven Development
Page 110: Strong Duck Type Driven Development
Page 111: Strong Duck Type Driven Development

Let’s build a duck

Page 112: Strong Duck Type Driven Development

Visual Cortex

Brain

Lungs Tongue Beak

duck nearby

Page 113: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do

end

end

Page 114: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }

end

end

Page 115: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex)

end

end

Page 116: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 117: Strong Duck Type Driven Development

class Duck

end

Page 118: Strong Duck Type Driven Development

class Duck

def initialize VisualCortex.new end

end

Page 119: Strong Duck Type Driven Development

Visual Cortex

Brain

duck nearby

Page 120: Strong Duck Type Driven Development

it “notifies the brain” do

end

Page 121: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new() visual_cortex.run

end

Page 122: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new() visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 123: Strong Duck Type Driven Development

class VisualCortex

def run brain.duck_nearby end

end

Page 124: Strong Duck Type Driven Development

class VisualCortex

def run brain.duck_nearby end

end

Page 125: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new() visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 126: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 127: Strong Duck Type Driven Development

it “notifies the brain” do brain = ?

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 128: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double(

)

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 129: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double( Contracts::Greeter )

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 130: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract end end

Page 131: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract confirm :duck_nearby => 0 end end

Page 132: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double( Contracts::Greeter )

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 133: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double( Contracts::Greeter )

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 134: Strong Duck Type Driven Development

class VisualCortex

def run brain.duck_nearby end

end

Page 135: Strong Duck Type Driven Development

class VisualCortex

def initialize(brain) @brain = brain end

def run @brain.duck_nearby end

end

Page 136: Strong Duck Type Driven Development

VisualCortex when there is duck nearby notifies the brain

Finished in 0.00082 seconds 1 example, 0 failures

Page 137: Strong Duck Type Driven Development

We haven’t created the brain yet

Page 138: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 139: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 140: Strong Duck Type Driven Development

ArgumentError: wrong number of arguments (0 for 1)

Page 141: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 142: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(Contract::Greeter) end

end

Page 143: Strong Duck Type Driven Development

class Duck

def initialize VisualCortex.new(?) end

end

Page 144: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(Contract::Greeter) end

end

Page 145: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a Brain” do expect { Duck.new }.to create(Brain). with(no_args) end

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(Contract::Greeter) end

end

Page 146: Strong Duck Type Driven Development

class Duck

def initialize VisualCortex.new(?) end

end

Page 147: Strong Duck Type Driven Development

class Duck

def initialize brain = Brain.new VisualCortex.new(brain) end

end

Page 148: Strong Duck Type Driven Development

class Brain end

Page 149: Strong Duck Type Driven Development

Lawyer::BrokenContract: Brain does not implement<Contracts::Greeter> (1 method missing) [missing] duck_nearby

Page 150: Strong Duck Type Driven Development

Lawyer::BrokenContract: Brain does not implement<Contracts::Greeter> (1 method missing) [missing] duck_nearby

Page 151: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do

end

end

Page 152: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do brain = Brain.new brain.duck_nearby

end

end

Page 153: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do brain = Brain.new brain.duck_nearby

expect(quacker).to receive(quack) end

end

Page 154: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do brain = Brain.new brain.duck_nearby

expect(quacker).to receive(quack) end

end

Page 155: Strong Duck Type Driven Development

The tests tell us what to write next

Page 156: Strong Duck Type Driven Development

Writing new code is easy

Page 157: Strong Duck Type Driven Development

Change is hard

Page 158: Strong Duck Type Driven Development

We should focus on making change easier

Page 159: Strong Duck Type Driven Development

We use TDD to make refactoring easier

Page 160: Strong Duck Type Driven Development

We use TDD to make refactoring objects easier

Page 161: Strong Duck Type Driven Development

Contracts make refactoring messages easier

Page 162: Strong Duck Type Driven Development

Think about the messages

Change the contract

Fix the failing specs

Page 163: Strong Duck Type Driven Development

Change the messages between objects

Page 164: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract confirm :duck_nearby => 0 end end

Page 165: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract confirm :duck_nearby => [:distance] end end

Page 166: Strong Duck Type Driven Development

Lawyer::BrokenContract:

Brain does not implement <Contracts::Greeter> (1 method with the wrong signature) [wrong signature] duck_nearby (missing [:distance])

Page 167: Strong Duck Type Driven Development

Failure/Error: visual_cortex.run

Double "Contracts::Greeter" received :duck_nearby with unexpected arguments

expected: ({:distance=>AnyArgMatcher}) got: (no args)

Page 168: Strong Duck Type Driven Development

Replace implementations

Page 169: Strong Duck Type Driven Development

Replace implementations(as long as they implement the same interface)

Page 170: Strong Duck Type Driven Development

class FrontalLobe def duck_nearby(distance:) end end

FrontalLobe.implements(Contracts::Greeter)

Page 171: Strong Duck Type Driven Development

class Duck

def initialize brain = Brain.new VisualCortex.new(brain) end

end

Page 172: Strong Duck Type Driven Development

class Duck

def initialize fl = FrontalLobe.new VisualCortex.new(fl) end

end

Page 173: Strong Duck Type Driven Development

The tests tell us what to write next

Page 174: Strong Duck Type Driven Development

We can trust the tests totell us what to write next

Page 175: Strong Duck Type Driven Development

We focus on isolated parts of the system

Page 176: Strong Duck Type Driven Development

Then we focus on messages

between parts of the system

Page 177: Strong Duck Type Driven Development

– Alan Kay

The big idea is "messaging"

http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html

Page 178: Strong Duck Type Driven Development

Recap

Page 179: Strong Duck Type Driven Development

Built a duck(badly)

Page 180: Strong Duck Type Driven Development

Noticed accidental coupling…

Page 181: Strong Duck Type Driven Development

…caused by ignoring the connections between objects

Page 182: Strong Duck Type Driven Development

…caused by ignoring the messaging between objects

Page 183: Strong Duck Type Driven Development

duck typing…Supercharged

Page 184: Strong Duck Type Driven Development

…to focus on connections between objects

Page 185: Strong Duck Type Driven Development

…to generate errors if we break those connections

Page 186: Strong Duck Type Driven Development

Use errors to drive our development

Page 187: Strong Duck Type Driven Development

Reduces accidental coupling

Page 188: Strong Duck Type Driven Development

Avoidsball-of-mud code

Page 189: Strong Duck Type Driven Development

🙋

Page 190: Strong Duck Type Driven Development

(strong (duck type)) driven development

Page 191: Strong Duck Type Driven Development

John Cinnamond @jcinnamond

panagile.com/talks/rubyconf-2014

Page 192: Strong Duck Type Driven Development

Attributions

Picture of Jakub Oboza used with permission and taken from http://lambdacu.be

The following images are all available under a CC Attribution 2.0 Generic licence. Many thanks to the original photographers for sharing these images.

‘~ Duck Dribble ~’ by Stuart Williams https://www.flickr.com/photos/viamoi/3336548665

‘Morgan 8’ Plus by Stewart Cambers https://www.flickr.com/photos/stewc/4392996817

‘Champion Bodybuilders in kuwait 2 June2010’ by Ra'ed Qutena https://www.flickr.com/photos/raedqutena/4665308585

‘duck pair walking’ by Sebastian Ziebell https://www.flickr.com/photos/zebel/2399225416