`
jlaky
  • 浏览: 42872 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Design Patterns in Ruby [Digest 7] Command

阅读更多

 

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics