Skip to content

Instantly share code, notes, and snippets.

@RedFred7
Created February 11, 2015 20:42
Show Gist options
  • Save RedFred7/d3638a2d15879806e679 to your computer and use it in GitHub Desktop.
Save RedFred7/d3638a2d15879806e679 to your computer and use it in GitHub Desktop.
Design Patterns the Ruby way: command
####### COMMAND PATTERN #############
### 1st attempt
## The following code implements the command pattern in the traditional
## manner, i.e. with a separate class for each command. We aim to
## implement a do / undo application that increments a global aggregate
## (or decrements it for the Undo)
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
## we now write some client code that creates a list of 10 Incrementer
## commands and then calls each one
puts "##### 1st attempt"
count = 0
commands = []
(1..10).each do |i|
commands << Incrementer.new(count)
end
puts "Count initially is: #{count}"
commands.each {|cmd| cmd.do_command}
## this will fail: the Inrcementer commands are not operating on the main-scope
## count but on a local (object-scope) copy. We need to bridge that scope gap.
puts "Count after doing commands: #{count}"
### the right way
## We now have a generic Command class that accepts do/undo commands in the form
## of Proc objects (closures). To execute a command, we just call its Proc.
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
## we now write the client code again
puts "#### the right way"
count = 0
commands = []
(1..10).each do |i|
## the count in each Proc refers to the count above, as the
## Proc is a closure, so it can access the variables in the
## same scope as the one it was defined in
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}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment