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
# Logic for turning on a specific device
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 # => puts "Device turned on!"
app.turn_on_tv # => puts "Device turned on!"
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.
# Create specific commands
turn_on_tv = TurnOnCommand.new(:tv)
turn_on_light = TurnOnCommand.new(:light)
# Create remote controller
remote_controller = RemoteController.new
# Add command to remote controller
remote_controller.add_command(:turn_on_light, turn_on_light)
remote_controller.add_command(:turn_on_tv, turn_on_tv)
# Use command
remote_controller.press_button(:turn_on_light) # => puts "Device turned on!"
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
# Logic for turning on a specific device
puts "Device turned on!"
end
def undo
# Logic for turning off specific devices
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.
# Create specific commands
turn_on_tv = TurnOnCommand.new(:tv)
# Create remote controller
remote_controller = RemoteController.new
# Add command to remote controller
remote_controller.add_command(:turn_on_tv, turn_on_tv)
# Use commands
remote_controller.press_button(:turn_on_tv) # => puts "Device turned on!"
remote_controller.press_undo_button(:turn_on_tv) # => puts "Device turned off!"