Created
February 11, 2015 20:42
-
-
Save RedFred7/d3638a2d15879806e679 to your computer and use it in GitHub Desktop.
Design Patterns the Ruby way: command
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
####### 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