Download - Design Patterns the Ruby way - ConFoo 2015
![Page 1: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/1.jpg)
DESIGN PATTERNS
THE WAY
![Page 2: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/2.jpg)
Hello ConFoo!I AM FRED HEATH
Developer, Problem
solver, Ruby evangelist,
Agile practitioner.
You can find me at:
@FredAtBootstrap
bootstrap.me.uk
![Page 3: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/3.jpg)
Let’s start with the
first set of slides
![Page 4: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/4.jpg)
The
gospel
according
to Russ
![Page 5: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/5.jpg)
1STRATEGY
![Page 6: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/6.jpg)
APPLICATIONS
▪ Dynamically choose algorithm
![Page 7: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/7.jpg)
Strategy
![Page 8: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/8.jpg)
class PaymentStrategy
def pay(sum)
raise "can't call that!"
end
end
class PayPalPayment < PaymentStrategy
def pay(sum)
puts ".....paying $#{sum} by PayPal"
end
end
class WorldPayPayment < PaymentStrategy
def pay(sum)
puts ".....paying $#{sum} by WorldPay"
end
end
class BitcoinPayment < PaymentStrategy
def pay(sum)
puts ".....paying $#{sum} by Bitcoin"
end
end
class Purchase
attr_reader :items, :sum
attr_accessor :payment_method
def initialize(items, payment_method)
@payment_method = payment_method
@sum = 0
items.each do |item, value|
@sum += value
end
end
def pay
payment_method.pay(@sum)
end
end
![Page 9: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/9.jpg)
# class PaymentStrategy
# def pay(sum)
# raise "can't call that!"
# end
# end
class PayPalPayment #< PaymentStrategy
def pay(sum)
puts ".....paying $#{sum} by PayPal"
end
end
class WorldPayPayment #< PaymentStrategy
def pay(sum)
puts ".....paying $#{sum} by WorldPay"
end
end
class BitcoinPayment #< PaymentStrategy
def pay(sum)
puts ".....paying $#{sum} by Bitcoin"
end
end
class Purchase
attr_reader :items, :sum
attr_accessor :payment_method
def initialize(items, payment_method)
@payment_method = payment_method
@sum = 0
items.each do |item, value|
@sum += value
end
end
def pay
payment_method.pay(@sum)
end
end
![Page 10: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/10.jpg)
$> purchase = Purchase.new({cd_Wild_Beasts: 5.2, baseball_cap: 8.5 }, WorldPayPayment.
new)
$> purchase.pay
.....paying $13.7 by WorldPay
$> purchase = Purchase.new({cd_Wild_Beasts: 5.2, baseball_cap: 8.5 }, PayPalPayment.
new)
$> purchase.pay
.....paying $13.7 by PayPal
![Page 11: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/11.jpg)
Lambda: an anonymous, first-class
function.
![Page 12: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/12.jpg)
def m(&a_lambda)
a_lambda.call
end
$> m {puts "this is a lambda"}
"this is a lambda"
![Page 13: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/13.jpg)
def m(&a_lambda)
x = 'hello'
a_lambda.call(x)
end
$> m {|x| puts "x is: #{x}"}
"x is: hello"
![Page 14: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/14.jpg)
class PayPalPayment
def pay(sum)
puts ".....paying $#{sum} by
PayPal"
end
end
class WorldPayPayment
def pay(sum)
puts ".....paying $#{sum} by
WorldPay"
end
end
class BitcoinPayment
def pay(sum)
puts ".....paying $#{sum} by
Bitcoin"
end
end
class Purchase
attr_reader :items, :sum
attr_accessor :payment_method
def initialize(items, payment_method)
@payment_method = payment_method
@sum = 0
items.each do |item, value|
@sum += value
end
end
def pay
payment_method.pay(@sum)
end
end
![Page 15: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/15.jpg)
#class PayPalPayment
# def pay(sum)
# puts ".....paying $#{sum} by
PayPal"
# end
#end
#class WorldPayPayment
# def pay(sum)
# puts ".....paying $#{sum} by
WorldPay"
# end
#end
#class BitcoinPayment
# def pay(sum)
# puts ".....paying $#{sum} by
Bitcoin"
# end
#end
class Purchase
attr_reader :items, :sum
attr_accessor :payment_method
def initialize(items, payment_method)
@payment_method = payment_method
@sum = 0
items.each do |item, value|
@sum += value
end
end
def pay
payment_method.pay(@sum)
end
end
![Page 16: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/16.jpg)
#class PayPalPayment
# def pay(sum)
# puts ".....paying $#{sum} by
PayPal"
# end
#end
#class WorldPayPayment
# def pay(sum)
# puts ".....paying $#{sum} by
WorldPay"
# end
#end
#class BitcoinPayment
# def pay(sum)
# puts ".....paying $#{sum} by
Bitcoin"
# end
#end
class Purchase
attr_reader :items, :sum
attr_accessor :payment_method
def initialize(items, &payment_method)
@payment_method = payment_method
@sum = 0
items.each do |item, value|
@sum += value
end
end
def pay
payment_method.call(@sum)
end
end
![Page 17: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/17.jpg)
$> purchase = Purchase.new({cd_Wild_Beasts: 5.2, baseball_cap: 8.5 }) {|sum| puts ".....
paying $#{sum} by WorldPay"}
$> purchase.pay
.....paying $13.7 by WorldPay
$> purchase = Purchase.new({cd_Wild_Beasts: 5.2, baseball_cap: 8.5 }) {|sum| puts ".....
paying $#{sum} by Amazon Payments"}
$> purchase.pay
.....paying $13.7 by Amazon Payments
![Page 18: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/18.jpg)
From:
![Page 19: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/19.jpg)
To:
![Page 20: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/20.jpg)
2COMMAND
![Page 21: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/21.jpg)
APPLICATIONS
▪ User Interfaces
▪ Queuing/Logging (Wizards)
▪ Do-Undo-Redo
![Page 22: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/22.jpg)
Command
![Page 23: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/23.jpg)
class Command
def do_command
raise "can't do this here"
end
def undo_command
raise "can't do this here"
end
end
class Incrementer < Command
def initialize(aggregate)
@aggregate = aggregate
end
def do_command
@aggregate += 2
end
def undo_command
@aggregate -= 2
end
end
![Page 24: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/24.jpg)
#class Command
# def do_command
# raise "can't do this here"
# end
# def undo_command
# raise "can't do this here"
# end
#end
class Incrementer #< Command
def initialize(aggregate)
@aggregate = aggregate
end
def do_command
@aggregate += 2
end
def undo_command
@aggregate -= 2
end
end
![Page 25: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/25.jpg)
count = 0
commands = []
(1..10).each do |i|
commands << Incrementer.new(count)
end
puts "Count initially is: #{count}"
commands.each {|cmd| cmd.do_command}
puts "Count after doing commands: #{count}
Count initially is: 0
Count after doing commands: 0
![Page 26: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/26.jpg)
Closure: A first-class function
that has lexical scope.
![Page 27: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/27.jpg)
outer = 1
def m a_var
inner = 99
puts "inner var = #{inner}"
proc {inner + a_var}
end
p = m(outer)
puts "p is a #{p.class}"
puts "result of proc call: #{p.call}"
inner var = 99
p is a Proc
result of proc call: 100
![Page 28: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/28.jpg)
outer = 1
def m a_var
inner = 99
puts "inner var = #{inner}"
proc {inner + a_var}
end
p = m(outer)
puts "p is a #{p.class}"
outer = 0
puts "changed outer to #{outer}"
puts "result of proc call: #{p.call}"
inner var = 99
p is a Proc
changed outer to 0
result of proc call: 100
![Page 29: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/29.jpg)
class Command
attr_accessor :cmd, :uncmd
def initialize(do_command, undo_command)
@cmd = do_command
@uncmd = undo_command
end
def do_command
@cmd.call
end
def undo_command
@uncmd.call
end
end
![Page 30: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/30.jpg)
count = 0
commands = []
(1..10).each do |i|
commands << Command.new(proc {count += 2}, proc {count -= 2})
end
puts "Count initially is: #{count}"
commands.each {|cmd| cmd.do_command}
puts "Count after doing commands: #{count}"
Count initially is: 0
Count after doing commands: 20
![Page 31: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/31.jpg)
count = 0
commands = []
(1..10).each do |i|
commands << Command.new(proc {count += 2}, proc {count -= 2})
end
puts "Count initially is: #{count}"
commands.each {|cmd| cmd.do_command}
puts "Count after doing commands: #{count}"
commands.reverse_each {|cmd| cmd.undo_command}
puts "Count after un-doing commands: #{count}"
commands.each {|cmd| cmd.do_command}
puts "Count after re-doing commands: #{count}"
Count initially is: 0
Count after doing commands: 20
Count after un-doing commands: 0
Count after re-doing commands: 20
![Page 32: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/32.jpg)
From:
![Page 33: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/33.jpg)
To:
![Page 34: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/34.jpg)
3PROXY
![Page 35: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/35.jpg)
APPLICATIONS
▪ Protection
▪ Remote Access
▪ Lazy Creation (Virtual Proxy)
![Page 36: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/36.jpg)
Proxy
![Page 37: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/37.jpg)
class Car
def drive
raise "use the Proxy instead"
end
end
class RealCar < Car
def drive
puts "vroom,vroom..."
end
end
class ProxyCar < Car
def initialize(real_car, driver_age)
@driver_age = driver_age
@real_car = real_car
end
def check_access
@driver_age > 16
end
def get_real_car
@real_car || (@real_car = Car.new
(@driver_age))
end
def drive
car = get_real_car
check_access ? car.drive : puts("Sorry,
you're too young to drive")
end
end
![Page 38: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/38.jpg)
class RealCar
def drive
puts "vroom,vroom..."
end
end
class ProxyCar
def initialize(real_car, driver_age)
@driver_age = driver_age
@real_car = real_car
end
def check_access
@driver_age > 16
end
def get_real_car
@real_car || (@real_car = Car.new
(@driver_age))
end
def drive
car = get_real_car
check_access ? car.drive : puts("Sorry,
you're too young to drive")
end
end
![Page 39: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/39.jpg)
class RealCar
def drive
puts "vroom,vroom..."
end
end
class Client
attr_reader :age
def initialize(age)
@age = age
end
def drive(car)
car.drive
end
end
class ProxyCar
def initialize(real_car, driver_age)
@driver_age = driver_age
@real_car = real_car
end
def check_access
@driver_age > 16
end
def get_real_car
@real_car || (@real_car = Car.new
(@driver_age))
end
def drive
car = get_real_car
check_access ? car.drive : puts("Sorry,
you're too young to drive")
end
end
![Page 40: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/40.jpg)
tom = Client.new(25)
car = RealCar.new()
proxy = ProxyCar.new(car, tom.age)
tom.drive(proxy)
vroom,vroom...
![Page 41: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/41.jpg)
tom = Client.new(15)
car = RealCar.new()
proxy = ProxyCar.new(car, tom.age)
tom.drive(proxy)
Sorry, you're too young to drive
![Page 42: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/42.jpg)
Dynamic Dispatching: selecting
which method to call at run-time
![Page 43: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/43.jpg)
puts [1, 2, 3].reverse
3
2
1
![Page 44: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/44.jpg)
puts [1, 2, 3].send(:reverse)
3
2
1
![Page 45: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/45.jpg)
BasicObject
Kernel
Object
MyClass
SuperClass
………..………..
a_method
![Page 46: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/46.jpg)
BasicObject
Kernel
Object
MyClass
SuperClass
………..………..
a_method
![Page 47: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/47.jpg)
BasicObject
Kernel
Object
MyClass
SuperClass
………..………..
a_method
![Page 48: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/48.jpg)
BasicObject
Kernel
Object
MyClass
SuperClass
………..………..
a_method
![Page 49: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/49.jpg)
BasicObject
Kernel
Object
MyClass
SuperClass
………..………..
a_method
![Page 50: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/50.jpg)
BasicObject
Kernel
Object
MyClass
SuperClass
………..………..
method_missing
![Page 51: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/51.jpg)
BasicObject
method_missing()
Kernel
Object
MyClass
SuperClass
………..………..
method_missingraise “Undefined method”
![Page 52: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/52.jpg)
class RealCar
def drive
puts "vroom,vroom..."
end
end
class Client
attr_reader :age
def initialize(age)
@age = age
end
def drive(car)
car.drive
end
end
class ProxyCar
def initialize(real_car, driver_age)
@driver_age = driver_age
@real_car = real_car
end
def method_missing(name, *args)
car = get_real_car
check_access ? car.send(name, *args) :
puts("Sorry, can't do this")
end
def check_access
@driver_age > 16
end
def get_real_car
@real_car || (@real_car = Car.new
(@driver_age))
end
end
![Page 53: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/53.jpg)
tom = Client.new(25)
car = RealCar.new()
proxy = ProxyCar.new(car, tom.age)
tom.drive(proxy)
vroom,vroom...
![Page 54: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/54.jpg)
Perfection [in design] is
achieved, not when there is
nothing more to add, but when
there is nothing left to take
away.
- Antoine de Saint-Exupéry
![Page 55: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/55.jpg)
CREDITS
Special thanks to all the people who made and released
these awesome resources for free:
▪ Busy Icons by Olly Holovchenko
▪ Presentation template by SlidesCarnival
▪ Photographs by Unsplash
![Page 56: Design Patterns the Ruby way - ConFoo 2015](https://reader034.vdocuments.site/reader034/viewer/2022051516/55a78d5f1a28ab306e8b48c1/html5/thumbnails/56.jpg)
GitHub Gists
▪ https://gist.github.
com/2809a0410ec452b64f4d
▪ https://gist.github.
com/d3638a2d15879806e679
▪ https://gist.github.
com/c1d7de9da194922305b2