Command pattern command is an instruction to do something, something specific. It can be execute right now or later or specific time. In our GUI's do and undo actions it is wildly used.
If we have a Button base class:
class SlickButton
#
# Lots of button drawing and management
# code omitted...
#
def on_button_push
#
# Do something when the button is pushed
#
end
end
There are many buttons that inherit from the SlickButton to overwrite the on_button_push method.
If some of the button's action is dynamically changing, the subclass will be an explosive increasing.
If we use Command:
class SlickButton
attr_accessor :command
def initialize(command)
@command = command
end
#
# Lots of button drawing and management
# code omitted...
#
def on_button_push
@command.execute if @command
end
end
The base command
class SaveCommand
def execute
#
# Save the current document...
#
end
end
Then the command is seperate from button, and the button can have any command they want even in runtime.
We can define many commands such as: CreateFile, DeleteFile, CopyFile and so on.
We can also build Composite command use Composite pattern:
class CompositeCommand < Command
def initialize
@commands = []
end
def add_command(cmd)
@commands << cmd
end
def execute
@commands.each {|cmd| cmd.execute}
end
def description
description = ''
@commands.each {|cmd| description += cmd.description + "\n"}
description
end
end
cmds = CompositeCommand.new
cmds.add_command(CreateFile.new('file1.txt', "hello world\n"))
cmds.add_command(CopyFile.new('file1.txt', 'file2.txt'))
cmds.add_command(DeleteFile.new('file1.txt'))
The undo command is really common, the Command pattern support it by define a undo method in the command class:
class CreateFile < Command
def initialize(path, contents)
super "Create file: #{path}"
@path = path
@contents = contents
end
def execute
f = File.open(@path, "w")
f.write(@contents)
f.close
end
def unexecute
File.delete(@path)
end
end
class DeleteFile < Command
def initialize(path)
super "Delete file: #{path}"
@path = path
end
def execute
if File.exists?(@path)
@contents = File.read(@path)
end
f = File.delete(@path)
end
def unexecute
if @contents
f = File.open(@path,"w")
f.write(@contents)
f.close
end
end
end
The composite command will be :
class CompositeCommand < Command
# ...
def unexecute
@commands.reverse.each { |cmd| cmd.unexecute }
end
# ...
end
The key thing about the Command pattern is that it separates the thought from the deed.
When you use this pattern, you are no longer simply saying, “Do this”; instead, you are saying, “Remember how to do this,” and, sometime later, “Do that thing that I told you to remember.”
Even in the lightweight code block-based renditions of the Command pattern available in Ruby, the two-part aspect of this pattern adds some serious complexity to your code.
Make sure that you really need that complexity before you pull the Command pattern out of your bag of tricks.
The best example of the Command may be the ActiveRecord Migrations with the self.up and self.down method that enable database level do migrate and undo migrate.
It it much easy to do database migration, the latest version of rails can support specific migration do and undo by recording all the migrate number.
Another great example is Madeleine