This talk showcases Design Patterns and the Ruby language. We'll provide an introduction to some of the capabilities that make Ruby unique and, at the same time, learn about some common design patterns. More importantly, we'll demonstrate how to use modern programming features like closures, lambdas and dynamic dispatching to re-think the way we implement design patterns.
8. 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
9. # 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
13. def m(&a_lambda)
x = 'hello'
a_lambda.call(x)
end
$> m {|x| puts "x is: #{x}"}
"x is: hello"
14. 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
15. #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
16. #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
23. 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
24. #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
25. 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
27. 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
28. 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
29. 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
30. 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
31. 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
37. 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
38. 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
39. 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
40. tom = Client.new(25)
car = RealCar.new()
proxy = ProxyCar.new(car, tom.age)
tom.drive(proxy)
vroom,vroom...
41. tom = Client.new(15)
car = RealCar.new()
proxy = ProxyCar.new(car, tom.age)
tom.drive(proxy)
Sorry, you're too young to drive
52. 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
53. tom = Client.new(25)
car = RealCar.new()
proxy = ProxyCar.new(car, tom.age)
tom.drive(proxy)
vroom,vroom...
54. 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
55. 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