Day 17: Command Pattern?—?Taking Orders and Executing Them Efficiently
Introduction
Welcome back, design pattern enthusiasts! Yesterday, we explored the Interpreter Pattern, empowering programs to understand external languages. Today, on Day 17 of our 30-Day Design Pattern Challenge, we’ll dive into the Command Pattern. This pattern offers a powerful way to encapsulate a request as an object, allowing for flexible queuing, logging, and undo/redo functionalities.
Problem
Imagine you’re developing a smart home system. Your task is to create a control panel with buttons for various operations, like turning lights on and off, adjusting the thermostat, and locking doors. You create a sleek Button class that can be used for buttons on the control panel, as well as for generic buttons in other parts of the app, like mobile or web interfaces.
All buttons in the app inherit from this base class. While they all look similar, each button needs to perform different actions. The simplest solution is to create many subclasses for each button, where each subclass contains the code to execute on a button click.
Lots of button subclasses. What can go?wrong?
Before long, you realize this approach is flawed. First, you end up with a huge number of subclasses, and making any change to the base Button class risks breaking the subclasses. Your GUI code becomes awkwardly dependent on the volatile business logic code.
Several classes implement the same functionality.
Some operations, like turning the lights on, need to be invoked from multiple places. For instance, you could click a small “Light On” button on the control panel, use a voice command via a smart speaker, or press a button on your mobile app.
Initially, when your app only had the control panel, it was okay to place the code for turning the lights on inside the LightOnButton subclass. But later, when you implement voice commands and mobile app buttons, you either have to duplicate the operation's code in many classes or make different interfaces dependent on the button subclasses, which is even worse.
Solution
Good software design often follows the principle of separation of concerns, breaking an app into layers. A typical example is having one layer for the graphical user interface (GUI) and another for the business logic. The GUI layer is responsible for displaying information on the screen and capturing user input. When it comes to performing important actions, like adjusting the thermostat or locking the doors, the GUI layer delegates the work to the underlying business logic layer.
In code, this might look like a GUI object calling a method of a business logic object, passing some arguments. This process is described as one object sending another a request.
The Command pattern suggests that GUI objects shouldn’t send requests directly. Instead, you should extract all request details (such as the target object, method name, and arguments) into a separate command class with a single method that triggers the request.
Command objects act as links between GUI and business logic objects. The GUI object doesn’t need to know what business logic object will handle the request or how it will be processed. It just triggers the command, which manages all the details.
Next, make your commands implement the same interface, usually with a single execute method that takes no parameters. This interface lets you use various commands with the same request sender without coupling it to concrete command classes. As a bonus, you can switch command objects linked to the sender, effectively changing the sender’s behavior at runtime.
You might wonder about request parameters. Since the command execute method doesn’t take parameters, how would you pass the request details to the receiver? The command should be either pre-configured with this data or capable of retrieving it on its own.
Applying the Command?Pattern
In our smart home system, after applying the Command pattern, you no longer need numerous button subclasses for different behaviors. Instead, put a single field in the base Button class to store a reference to a command object and make the button execute that command on a click.
You’ll implement various command classes for every possible operation and link them with specific buttons based on their intended behavior.
领英推荐
Other GUI elements, such as voice commands or mobile app buttons, can be implemented similarly. They’ll be linked to a command which gets executed when the user interacts with the GUI element. Elements related to the same operations will be linked to the same commands, preventing code duplication.
As a result, commands form a convenient middle layer that reduces coupling between the GUI and business logic layers. And that’s just one of the many benefits the Command pattern offers!
Class Diagram
The class diagram consists of the following entities
Implementation Example
Let’s illustrate the Command Pattern with a simple light switch example.
interface Command {
void execute();
}
class Light {
public void turnOn() {
System.out.println("Light turned on");
}
public void turnOff() {
System.out.println("Light turned off");
}
}
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
}
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
}
class Switch {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void press() {
if (command != null) {
command.execute();
}
}
}
Benefits of the Command?Pattern
When to Use the Command?Pattern
Real-World Examples
Conclusion
The Command Pattern emerges as a versatile tool in a software developer’s arsenal, offering a structured approach to encapsulating and managing requests. By decoupling the request from its execution, you create systems that are more flexible, extensible, and maintainable. Whether you need to support undo/redo functionalities, queuing, or complex macro actions, the Command Pattern provides a robust foundation.
By mastering this pattern, you’ll gain the ability to design systems that are not only efficient but also adaptable to evolving requirements. So, embrace the power of the Command Pattern and elevate your software development practices!
Join me tomorrow as we explore another essential design pattern on our journey. Feel free to leave comments or questions below. If you enjoyed this blog, consider giving it a clap ??!
Stay tuned for Day 18!
Actively looking for Full-time Opportunities in AI/ML/Robotics | Ex-Algorithms & ML Engineer @ Dynocardia Inc | Computer Vision Research Assistant & Robotics Graduate Student @Northeastern University
7 个月Very Informative ??