Command Pattern
Used to decouple a sender (aka invoker) from a receiver during a command execution.
The Command Pattern has a wide variety of use cases since by decoupling the sender and the receiver in such a way that they only interact through a completely customizable interface so there is actually a lot that can be done due to this decoupling just by tweaking the interface.
Application : Literally every modern framework has Command Pattern implemented in some form or the other due the versatile application of this logic. It gives the developers (invoker) that use this framework complete customizability without actually needing to care what runs on the receiver side.
Other uses :
- Implement a middleware functionality in framework using Composite Command architecture
- Implement undo, redo mechanisms by storing command objects that contain the logic to restore a particular state.
UML Explanation....
At its core, the command pattern consists of 4 interacting bodies
Body | Function |
---|---|
Invoker |
Interacts with the interface to call a specific function on the receiver side |
Command |
It's the interface establishes and controls communication between the sender and the receiver. It declares an execute( ) method |
Receivers |
Classes that expose methods that hold the actual implementation logic of a receiver service (eg : AddService, AuthService etc.) |
ConcreteCommand |
A child class of the interface that defines the execute( ) method and which receiver service it shall call. |
Resources
The source code for all prominent design patterns has been properly implemented and documented in this GitHub repostitory by me.
Project Description
Here, in this article, we will be building a simple application that decouples customer handling of any major services, be it Amazon or Netflix or Uber or any other company that has user authentication and management set up !!🔥
Implementing the Command (Interface)
An interface that establishes and manages communication between the sender and receiver
- On some request from sender, it creates a Command object
- Also, it declares the execute( ) method which shall be defined on the receiver end to run the logic that answers the sender's request.
Implementing the Receivers
These are simply class that expose methods holding some implementation logic (eg : adding a customer, authenticating a customer etc.), to anyone that instantiates them
Class Name | Method exposed | Function |
---|---|---|
AddCust | addCust() |
Method for implementing the addition of a new use to a database |
AuthCust | authCust() |
Method for implementing the authentication of an existing user |
Implementing the ConcreteCommand
A child class of the Command interface.
It does 2 things :
- It instantiates all the Receiver classes so that it can access the methods they expose
- It defines the execute( ) method by populating it with the method call to a Receiver service using the objects of the Receiver classes
Here, we simply execute the logic for the addCust( ) or authCust( ) method by simply calling them on the objects of the AddService or AuthService class that we instantiated here.
Implementing the Invoker
A simple class that gives out an object that developers using this pattern use to execute some receiver function.
- Here, we simply create a Button class that calls the execute( ) method on the Command object.
- On clicking this Button object (instance of our Button class), a Command object is created and the execute( ) method is called on it
Using this pattern in our application
Import all the necessary classes and modules into main.java for a final showdown of our application ! Here, I'll be show the addition as well as authentication part !
var addService = new AddService();
var authService = new authService();
var command = new AddCommand(addService, authService);
var button = new Button(addCommand);
button.click();
Here, I have done 2 things that can be changed when scaling up :
- I have called both the services for adding and authenticating a customer but you may choose to use an input from sender coupled with a switch statement inside the ConcreteCommand to conditionally call whichever receiver service you want
- You can make the receiver services extend a main Receiver interface or parent class to introduce more abstraction of the instances created in the ConcreteCommand to avoid any confusions as the size of your codebase grows because you start offering more receiver services
Conclusion
Congrats on unlocked the secret of how most modern frameworks work under the hood. You should feel pretty comfortable now developing the your own frameworks functionalities in a that contain the right balance of abstraction and control. Until next time...
I shall also be writing some content on using the Command Pattern to implement Composite Commands and Undo, Redo functions so do check them out !!
References..
Whatever I have learnt and implemented here in code as well as the explanation is all from the teachings of the brilliant Mosh Hamedani and his course on Design Patterns which I would recommend you to take if you are really interested in exploring the beauty of Object Oriented Programming