taskCommand
The Command Pattern is a behavioral design pattern where an object is used to encapsulate all the information needed to perform an action. This pattern allows you to decouple sender and receiver objects, providing a more flexible and extensible design.
We will pass all the logic related to our task to the TurnOnCommand class, which will be an implementation of the Command pattern.
class TurnOnCommand
attr_reader :device
def initialize(device)
@device = device
end
def execute
puts "Device turned on!"
end
end
When the Command is ready, we can deal with our transmitters. The first of them will be an application that will have two methods turn_on_light and turn_on_tv.
class Application
def turn_on_light
TurnOnCommand.new(:light).execute
end
def turn_on_tv
TurnOnCommand.new(:tv).execute
end
end
An Application with this structure basically only delegates tasks to our Command, allowing us to turn on both the TV and the light.
app = Application.new
app.turn_on_light
app.turn_on_tv
For our next transmitter, we will build a slightly more advanced structure that will allow us to add new commands in a more dynamic way.
class RemoteController
attr_reader :commands
def initialize
@commands = {}
end
def add_command(name, command)
@commands[name] = command
end
def press_button(name)
command = commands[name]
command.execute if command
end
end
Now we can add new commands dynamically.
turn_on_tv = TurnOnCommand.new(:tv)
turn_on_light = TurnOnCommand.new(:light)
remote_controller = RemoteController.new
remote_controller.add_command(:turn_on_light, turn_on_light)
remote_controller.add_command(:turn_on_tv, turn_on_tv)
remote_controller.press_button(:turn_on_light)
Let’s imagine that we now want to be able to turn off the TV. Probably the first thought that comes to your mind is to create a new TurnOffCommand class. It could be implemented this way, but the Command pattern is often implemented with two methods, one is the one you created, which is responsible for executing the task, and the second one allows you to reverse the effect of the first method.
So let’s add an undo method.
class TurnOnCommand
attr_reader :device
def initialize(device)
@device = device
end
def execute
puts "Device turned on!"
end
def undo
puts "Device turned off!"
end
end
Of course, we also need to add a new method that allows us to use the undo method in the controller.
class RemoteController
def press_undo_button(name)
command = commands[name]
command.undo if command
end
end
Now we can easily add a new function to our controller.
turn_on_tv = TurnOnCommand.new(:tv)
remote_controller = RemoteController.new
remote_controller.add_command(:turn_on_tv, turn_on_tv)
remote_controller.press_button(:turn_on_tv)
remote_controller.press_undo_button(:turn_on_tv)