Welcome back! In the previous lessons, we explored various behavioral patterns like the Strategy and Observer Patterns. This time, we'll dive into the Command Pattern, a powerful technique that encapsulates a request as an object, thereby allowing for the parameterization and queuing of requests.
In this lesson, you will discover how to implement the Command Pattern in Go, which will enable you to decouple the sender and receiver of a request. Specifically, you'll learn to:
Let's take a peek at an example, which we'll break down step by step:
We start by defining a Command
interface with an Execute
method:
Go1// Command interface 2type Command interface { 3 Execute() 4}
Next, we create concrete commands that implement the Command
interface. The LightOnCommand
and LightOffCommand
structs represent turning a light on and off, respectively, and implement the Execute
method to print the corresponding message when executed:
Go1// Concrete Command for turning on the light 2type LightOnCommand struct {} 3 4func (c *LightOnCommand) Execute() { 5 fmt.Println("The light is turned on.") 6} 7 8// Concrete Command for turning off the light 9type LightOffCommand struct {} 10 11func (c *LightOffCommand) Execute() { 12 fmt.Println("The light is turned off.") 13}
We then create an invoker, Button
, that holds a command and triggers it when needed. The Press
method executes the command stored in the invoker. Therefore if the invoker holds the LightOnCommand
, pressing the button will turn the light on:
Go1// Invoker 2type Button struct { 3 command Command 4} 5 6// NewButton creates a new Button 7func NewButton(command Command) *Button { 8 return &Button{command: command} 9} 10 11// SetCommand method for Button 12func (b *Button) SetCommand(command Command) { 13 b.command = command 14} 15 16// Press method for Button which triggers the command 17func (b *Button) Press() { 18 b.command.Execute() 19}
Finally, we demonstrate how to use the Command Pattern in the main
function by creating concrete commands, setting them on the invoker, and triggering them using the invoker:
Go1func main() { 2 lightOn := &LightOnCommand{} 3 lightOff := &LightOffCommand{} 4 5 button := NewButton(lightOn) 6 button.Press() // Output: The light is turned on. 7 8 button.SetCommand(lightOff) 9 button.Press() // Output: The light is turned off. 10}
As you see in the output comments, the Command Pattern allows us to encapsulate actions as objects, making it easy to parameterize and execute them.
Let's break down the key components of the Command Pattern:
Execute
method is used to trigger the command.LightOnCommand
and LightOffCommand
are concrete commands that turn the light on and off, respectively.Button
struct acts as an invoker that holds a command and triggers it when needed. The Press
method executes the command.main
function demonstrates how to create concrete commands, set them on the invoker, and trigger them using the invoker.The Command Pattern is widely used in various scenarios, such as:
Let's explore the advantages and disadvantages of using the Command Pattern:
Pros
Cons
The Command Pattern is crucial in software design for several reasons:
Invoker
) from the object that performs the action (Receiver
).For example, think about a remote control for a smart home. Each button on the remote could encapsulate commands like turning lights on or off. By using the Command Pattern, we can easily add new functionalities, such as dimming lights or adjusting the thermostat, without changing the remote's existing functionality.
Ready to see the Command Pattern in action? Let's dive into the practice section and solidify your understanding!