Reusable solutions for typical software design issues that arise during development are design patterns. They offer proven solutions, encapsulating best practices and design principles to improve code quality, maintainability, and scalability. Design patterns provide developers with a common vocabulary to communicate and share solutions to recurring design challenges.
From this blog, you will:
- Get a basic understanding of what command design pattern in Apex is all about.
- Get an in-depth understanding of all the core concepts of command design patterns.
- Discover the benefits of using command design patterns.
Is Command a Design Pattern?
The Command Design Pattern stands out among the many design patterns as a behavioural pattern that concentrates on encapsulating a request as an object. This encapsulation allows decoupling of the sender (client code that initiates the request) and the receiver (code that performs the actual action). The Command pattern encourages software systems’ modularity, flexibility, and extensibility in this way.
The Command Design Pattern belongs to the behavioural design pattern category, emphasising the interaction between objects and their responsibilities. Behavioural patterns focus on assigning responsibilities between entities and the flow of communication among them. Within the behavioural pattern category, the Command pattern specifically addresses how requests are encapsulated, queued, and executed, offering an elegant way to handle complex command structures and undo functionality.
Core Concepts of the Command Design Pattern
The Command Design Pattern is a fundamental behavioural pattern that provides a structured approach to managing and executing requests flexibly and decoupled. This pattern comprises several key concepts that collectively contribute to its effectiveness in software design.
Command Objects: Command objects lie at the heart of the Command Design Pattern. They encapsulate representations of a specific request or action a client wishes to perform. By encapsulating requests within objects, the Command pattern achieves a level of abstraction that allows for easy manipulation and handling of commands. This encapsulation offers two significant advantages:
Encapsulation of a Request as an Object: With the Command pattern, requests are no longer simple method calls. Instead, they are transformed into full-fledged objects that encapsulate all the necessary information about the request. This includes details such as the action to be performed, the parameters required, and the context in which the request is executed.
Separation of Sender and Receiver: One of the primary benefits of the Command pattern is the separation it introduces between the sender of a request and the receiver that performs the actual operation. This decoupling is achieved by having the sender create and pass a command object to the receiver. The receiver, responsible for knowing how to execute the command, can be entirely distinct from the sender.
Invoker: The invoker acts as an intermediary between the sender and the receiver, ensuring that the command is executed without needing to know the specifics of the command itself. This separation of concerns simplifies the overall architecture and promotes a clear division of responsibilities.
AccountCreator invoker class:
public class AccountCreator {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void createAccount() {
// Unique code to invoke the command’s execute method
command.execute();
}
}
Responsible for Invoking Commands Without Knowing Their Details: The invoker’s role is to take a command object and trigger its execution. This action can be initiated by the client (sender) or other system components. Importantly, the invoker doesn’t need to know exactly how the command will be executed; it simply invokes the execute() method on the command object.
Receiver: The receiver is responsible for carrying out the actual operations associated with a command. It knows how to perform the necessary actions and may interact with the rest of the system to complete the request.
Executes the Actual Operations Associated with Commands: When the invoker triggers the execution of a command, the receiver steps in to perform the intended action. This could involve changing the system’s state, interacting with various components, or initiating further orders. The receiver’s knowledge of executing the command ensures that the sender remains decoupled from the implementation details.
Also Read: DataWeave In Apex
Implementing the Command Design Pattern in Apex
Implementing the Command Design Pattern in Apex involves systematically encapsulating and executing commands within your codebase. This pattern offers a structured solution for managing complex interactions and promoting flexibility in your software.
Defining the Command Interface
Declare an Interface for Executing Commands: Define an interface that outlines the methods required to execute a command. This interface is a contract that all concrete command classes will adhere to. It typically includes a method like execute() that represents the action to be performed.
Creating Concrete Command Classes
Creating concrete command classes that implement the interface defined in the previous step. Each command class encapsulates a specific action or operation. For example, you might have command classes like CreateOrderCommand, CancelOrderCommand, or UpdateInventoryCommand. These classes hold the necessary information and logic to perform their respective actions.
CreateAccountCommand concrete class:
public class CreateAccountCommand implements Command {
private Account account;
public CreateAccountCommand(AccountReceiver accountReceiver) {
this.account = accountReceiver.getAccount();
}
@Override
public void execute() {
// Unique code to perform the account creation action
AccountDatabase.insert(account);
}
}
Developing Receiver Classes
Receiver classes execute the actual operations associated with commands. They are the recipients of command objects and know how to interpret and fulfil the requests. For instance, an OrderProcessor class could be a receiver that handles order-related commands by interacting with the database or other components.
AccountReceiver receiver class:
public class AccountReceiver {
private Account account;
public AccountReceiver(String name, Double amount) {
this.account = new Account(name, amount);
}
public Account getAccount() {
return account;
}
}
Setting Up the Invoker
Maintain a Collection of Commands: The invoker maintains a collection of command objects in a list or a queue. This collection holds the commands that need to be executed. By limiting communication with command objects to the Command interface, it dissociates the sender and recipient.
Execute Commands Through the Invoker: The invoker’s main responsibility is to execute the stored commands when necessary. It doesn’t need to know the specifics of the command logic; it simply invokes the execute() method on each command object in its collection.
Benefits of the Command Design Pattern:
The Command Design Pattern brings several notable benefits to software development, making it a valuable tool for structuring code and managing complex interactions. These advantages contribute to improved modularity, maintainability, and overall code quality.
- Decoupling Sender and Receiver: The Command pattern fosters loose coupling between the sender of a request and the receiver that executes the request. This separation allows for changes to one component without affecting the other, enhancing the flexibility to modify and extend the system.
- Support for Undo/Redo Functionality: By encapsulating the state changes caused by command execution within the command objects, the pattern facilitates undo and redo functionality implementation. This is very helpful in programs that allow users to undo or undo previously performed activities.
- Logging and Auditing Actions Command objects can be designed to capture information about the executed commands. This capability is valuable for logging purposes, enabling the tracking and auditing of actions performed within the system. Detailed records of executed commands can aid in troubleshooting and analyzing system behaviour.
- Queuing and Scheduling Commands: The Command pattern provides a natural way to manage queues of commands. This is beneficial when commands must be executed in a specific order or at particular times. Command queues can be used to manage background jobs, schedule tasks, or orchestrate complex workflows.
Conclusion
Although the Command pattern has several advantages, it should only be used when necessary. Complex systems may benefit from the pattern’s structure, but simple systems might not warrant the overhead of command objects. Consider whether the Command pattern fits with your design objectives after assessing the unique requirements of your application.
Join saasguru on Slack community to explore the world of Salesforce. It is a place for open discussion and getting the latest updates on the Salesforce ecosystem.
You can also start your Salesforce journey by exploring the saasguru platform to discover certification courses and unlock limited offers. Whether you want to become a Salesforce Developer or Salesforce Consultant we have it all listed on our platform. Start your journey today!