Ruby中的命令模式

时间:2017-04-21 06:28:33

标签: ruby design-patterns solid-principles command-pattern

我喜欢Ruby和我学习设计模式。这是Command Pattern的一个例子。域模型如下:

  • 命令 - 所有命令的通用接口
  • OnCommand OffCommand - 实现Command界面的特定命令,都将TV对象用作接收器
  • 按钮 - 使用Command对象获取结果
  • RemoteContol - 客户端,初始化Button和Command对象 按钮 - 使用Command对象执行命令
  • 电视 - 接收命令

以下是此问题的一些代码:

class TV
  def on
    puts 'TV is on'
  end

  def off
    puts 'TV is off'
  end
end

class Command
  class Error < StandardError; end

  def execute
    raise Error
  end
end

class OnCommand < Command
  attr_reader :tv

  def initialize(tv)
    @tv = tv
  end

  def execute
    tv.on
  end
end

class OffCommand < Command
  attr_reader :tv

  def initialize(tv)
    @tv = tv
  end

  def execute
    tv.off
  end
end

class Button
  attr_reader :command

  def initialize(command)
    @command = command
  end

  def execute_command
    command.execute
  end
end

class RemoteControl
  attr_reader :tv

  def initialize(tv)
    @tv = tv
    @buttons = {
      on: Button.new(OnCommand.new(tv)),
      off: Button.new(OffCommand.new(tv))
    }
  end

  def use_button(name)
    button(name).execute_command
  end

  private

  def button(name)
    @buttons.fetch name
  end
end

以下是用法示例:

tv = TV.new                                                                                                             
remote_control = RemoteControl.new(tv)                                                                                  
remote_control.use_button(:on) # TV is on                                                                                         
remote_control.use_button(:off) # TV is off 

我对Command Pattern有多了解?这是一个很好的例子吗?

感谢任何想法

已更新

我想用 InfraredTransmitter 替换电视,因为命令接收器会使示例更合适。因为只有电视本身可以执行&#34; on&#34;命令。 RemoteControl可以物理访问其红外发射器,可以向电视发送不同的命令。

3 个答案:

答案 0 :(得分:1)

Command不应该引用TV。这是一个命令,它是一个 tv-only 命令。这是更多的红宝石方法:

class Command
  def call(*args, &cb)
    target, command, *rest = *args
    target.public_send command, *rest, &cb
  end
end

%i|on off|.each do |command|
  const_set("Command#{command.to_s.capitalize}", Class.new(Command) do
    def call(*args, &cb)
      target, *rest = *args
      super(target, command, *rest, &cb)
    end
  end
end

class Button
  attr_reader :command

  def initialize(command) # accepts :on, "on", CommandOn
    @command = case command
               when Class then command
               when Symbol, String
                 const_get("Command#{command.to_s.capitalize}")
               end.new
  end

  def call(target, *args, &cb)
    command.(target, *args, &cb)
  end
end

class RemoteControl
  def initialize(tv)
    @tv = tv
    @buttons = %i|on off|.map { |c| [c, Button.new(c)] }.to_h
  end
  def call(command)
    @buttons[command].(@tv)
  end
end

remote_control = RemoteControl.new(TV.new)
remote_control.(:on)
remote_control.(:off)

使用这种方法,Command可能会被重用于任何其他目标,而无需声明新的Command必须目标不可知,否则它不是通用的Command模式。

正如@Aetherus指出的那样,在这种方法中,根本不需要超类。可以安全地删除上面的Command,除非你有一些共同的逻辑可以放在那里。

答案 1 :(得分:0)

对我来说是对的。
Command模式有以下参与者:

命令 - 声明用于执行操作的界面
ConcreteCommand - 定义Receiver对象与动作之间的绑定
客户端 - 创建一个ConcreteCommand对象并设置其接收方
祈求者 - 要求命令执行请求
接收方 - 知道如何执行与执行请求相关的操作

因此,在您的示例中,命令命令 OnCommand OffCommand ConcreteCommand( s)电视 接收器按钮 投影仪 RemoteControl 客户

答案 2 :(得分:0)

您不需要超类Command。只需定义那些具体的命令。那就是动态编程语言的运作方式。他们不关心类型。他们只关心对象是否具有所需的方法。