This content originally appeared on Bits and Pieces - Medium and was authored by Fernando Doglio
The Command Pattern in TypeScript — Encapsulating Logic to Increase Maintainability
Learn how you can abstract complex logic into individual components with a few lines of code
Design patterns are there to keep us from reinventing the wheel, algorithmically speaking, that is.
They provide us with a template of how to best structure our code and how each component should communicate with each other.
And the command pattern is no exception, giving us a way to encapsulate part of our logic while decoupling from the rest of the project. This is by far, one of my favorite design patterns and this is why I wanted to cover it here.
What is the Command Pattern?
Long story short, the command pattern is a behavioral pattern (meaning, it takes care of solving how objects behave or interact with each other) that consist of encapsulating the logic required to execute an action inside an object. This logic can then be executed by client code whenever is required (i.e when an event is triggered) without having to know anything about the action itself.
It is a nice way to abstract logic so that you can later replace it or re-use it as you see fit.
For instance, let’s take a look at a silly example: imagine you’re working on a low-level implementation of a logic that requires you to interact with an OS module. As part of your requirements, you’ll have to perform actions with files, such as renaming them, copying them, or even joining 2 together.
Now, one option would be to simply have your code directly call the required OS methods, and perform the actions you’d need. That would couple your code to the OS module’s implementation. If that module changed tomorrow, your logic would be directly affected.
However, you could also implement something like this:
Those three classes individually implement a single action each, and they all implement the same interface as well, so you could potentially write code that would use them like this.
The executor function, while oversimplified here, doesn’t really care about what they’re doing or how they’re doing it. It just cares that whatever object it receives has the right shape (by implementing the right interface).
We can also look at it from a high-level perspective with the following diagram:
You can see there clearly how the client code is only interested in the ICommand interface, the actual commands only worry about implementing it. All three commands using the OS module is also quite normal, after all, the commands themselves are just a set of actions around other services used to abstract the complexity of using them inside an independent and singular component.
What’s the benefit of this?
- Each action can be as complex as needed and because it’s modularized, they can be worked on by different people, even different teams.
- Each action is going to be separated into its own file usually, which means better organization, and requires fewer modifications when adding new ones.
- Extensibility-wise, you can see that it’s as simple as adding a new class that implements the ICommand interface, that’s all it takes.
- The alternative (having all actions as part of a single class) could potentially create a very complex definition given complex enough actions. On the contrary, through this pattern we’re isolating that complexity into its own container.
Use cases for the Command Pattern
The above example is good to showcase how easy the coding part of implementing the Command pattern is, but where can you really use it?
And the answer is: many places!
- Plugin development. Consider what a plugin is: an encapsulated behavior that you can inject into existing client code that will execute it, without really having any context about its internal logic. That fits perfectly inside the command pattern.
- Undo actions. Every command has a very distinct logic and it can also keep a state associated with that logic. For example, the state of the object you’re using or modifying through your command right before the command’s execute method is called. That way, you can also add an undo method, that will reverse the changes done by the command. This pattern makes implementing the “Undo” functionality surprisingly easy.
- Creating workflows. Imagine having to perform multiple tasks in a set order. Or wanting the ability to queue multiple actions and execute them one by one. That can easily be done by encapsulating said actions into their own objects and then dealing with them as if they were “first-class citizens” of your problem domain. That is the command pattern right there. The client code is used to orchestrate a set of independent commands thanks to the fact that they all share the same interface.
- UI actions that are independent of their visual component. Put another way, you can save a file by pressing CTRL+S or going to the File menu, then clicking on “Save”, or you can even click on the floppy disk icon on your menu bar. The action remains the same: saving the file locally, but the starting point is different. That can be achieved by encapsulating the underlying logic of the action in one place (a Command) and having its execution be triggered from wherever it might be needed. The “hard part” here is coding the action’s logic, the rest is just calling a method.
As you can see, this pattern is very versatile and super useful for our everyday tasks, no matter if you’re a back-end developer, front-end or an IoT dev, whatever you do, it can be used within your context.
The command pattern is versatile, helps keep the code clean and easy to maintain. This is why I love using it whenever I can. Besides, it’s not that hard to implement either, as long as you have a correctly defined interface, the rest is a piece of cake!
Have you used this pattern before? What for? Leave a comment below and share your opinion about it!
Build & share JavaScript components/modules with Bit
Bit is an extensible tool that lets you create truly modular applications with independently authored, versioned, and maintained components.
Use it to build modular apps, author and deliver microservices and micro frontends, or simply share components between applications.
Bit: The platform for the modular web
Learn More
- The Composite Pattern for TypeScript Developers
- The Expand and Contract Pattern in JavaScript
- My Favorite Microservice Design Patterns for Node.js
The Command Pattern in TypeScript — Encapsulating Logic to Increase Maintainability was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Fernando Doglio
Fernando Doglio | Sciencx (2021-07-08T15:11:28+00:00) The Command Pattern in TypeScript — Encapsulating Logic to Increase Maintainability. Retrieved from https://www.scien.cx/2021/07/08/the-command-pattern-in-typescript%e2%80%8a-%e2%80%8aencapsulating-logic-to-increase-maintainability/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.