Combining the Command Pattern With State Pattern in JavaScript | by jsmanifest | Nov, 2022

commandification

Photo by Mikhail Nilov

JavaScript is a popular language known for its flexibility. Thanks to this, it makes it easy to implement patterns like the Command Pattern into our apps.

When a design pattern goes well hand in hand with the state pattern, it is arguably the command pattern.

If you read one of my previous blog posts about the State pattern, you may remember this sentence: “The State pattern ensures that an object can execute predictable, coordinated methods based on the current “state” of the application. deals with.”

In the command pattern, the main goal is to separate communication between two important participants:

  1. Initiator (also called Invoker)
  2. handler

We will cover Command Pattern and State Pattern in this post. If you are learning either one, my best advice to get the most out of this post is to make sure you understand the flow of the state pattern before continuing with the implementation of the command pattern so that To get a better feel for how the behavior of the code changes drastically while maintaining functionality.

Let’s start with an example using the State pattern to see this more clearly. Here’s the code:

In the above example, we have a state And subscribers Thing. subscribers The object holds a collection of callback functions. These callback functions are called whenever setState function is called. Here’s what it looks like:

function setState(newState)  prevState = State state = type newState === 'function' ?  newState(prevState) :  …prevState, …newState  notifySubscribers(prevState, state)

Every time the state updates, all registered callbacks are called with the previous state (prevState) and new State (newState) in arguments.

We have registered a callback listener so that we can see the state updates and update the background color whenever the number of profiles is a certain length, The table below shows a clear picture of lining up the count of the profile with the respective colour:

Minimum ThresholdBackground Color0white5blue9orange10red

Note: Code here is taken from previous code block

subscribe(
(function ()
function getColor(length)
if (length >= 5 && length <= 8) return 'blue' // Average
if (length > 8 && length < 10) return 'orange' // Reaching limit
if (length > 10) return 'red' // Limit reached
return 'white' // Default

return (prevState, newState) => []
const newProfiles = newState?.profiles
)(),
)

So, how can the command pattern fit into this? If we look at our code, we can see that we have defined some functions responsible for calling and handling this logic. It looks like this:

Instead we can abstract these into commands. The upcoming code example will show similar code with the command pattern applied corresponding to the state pattern. When we do this, only two functions are left untouched: setState And subscribe,

Let’s go ahead and introduce the command pattern and command our abstract functions like this:

It is now very clear to determine which functions we need to update the state. We can separate everything into their separate command handlers. This way, we can separate them into their own separate file or location to work with them more easily.

Here are the steps shown in our updated example to get to that point:

  1. create commands Char. It will store the registered commands and their callback handlers.
  2. define registerCommand, This will register new commands and their callback handlers commands Thing.
  3. define dispatch, It is responsible for calling the callback handler associated with their command.

With these three steps, we are all set up to have our commands registered by the client code, allowing them to implement their own commands and logic. notice how our registerCommand And dispatch Functions don’t need to be aware of anything related to our state objects.

We can easily take advantage of this and keep separating them into a separate file:

commands.js

As per the actual logic written in these lines, here is what it looks like:

Normally, this would all be left to the client code to decide (technically, our last code snippet represents client code). Some library authors also define their own command handlers for internal use, but the same concept applies.

A practice I often see is putting their internal logic in a separate file, prefixed with the filename of "internal" (Example: internalCommands.ts, Worth noting that 99% of the time, the functions in those files are never exported to the user. That’s why they are marked internal.

The image below is a diagram of what our code looks like before we apply the Command Design Pattern:

Purple colored bubbles represent functions. two tasks setBackgroundColor And addProfile are included in them. specifically for both of them, however, call setState To facilitate the change of state directly. In other words, they call and handle the state update logic for the specific slice of state they are interested in.

Now look at the diagram below. This image shows how our code looks like after applying the pattern:

Work notifySubscribers, addProfileAnd setBackgroundColor are gone, but all their arguments remain. They are now written as command handlers:

Command handlers define their arguments separately and get registered. Once they are registered, they are “put on hold” until they are called by dispatch Celebration.

Ultimately, the functionality of the code remains the same, and only the behavior changes.

One example that immediately comes to my mind is literal package by facebook Here, Lexical is an “extensible JavaScript web text-editor framework with an emphasis on reliability, accessibility, and performance.”

In Lexical, the commands for the editor can be Lodged and becomes available for use. handling logic is defined when they are registered so that they can be recognized for dispatch Call.

I hope you find it valuable. See more in the future!

Leave a Reply