Multifactor Authentication and Its Benefits
January 23, 2020Syntax Highlighting
February 19, 2020Classes are a syntactic sugar element in modern JavaScript. This syntax was proposed by proponents of Class based languages such as Java (which ironically has nothing to do with JavaScript directly). Classes simplify the instantiation process that JavaScript’s Object prototype system had, which was quite detailed and cumbersome. Objects are still prototype based, though. They are not truly class based.
Recently, I’ve been using Classes more extensively. First, I used them as data entities within my business logic APIs. More recently, I’ve used them as state managers for complex processes. In this sense, they act as a state management tool for processes that perform multiple functions and can succeed or fail in a variety of ways in any or all layers.
Here’s an example of a simple state management style Class:
class ProcessStateManagement {
constructor(dataToProcess) {
this.sizeOfDataToProcesss = (dataToProcess) ? sizeOfData(dataToProcess) : 0;
this.errors = [];
this.dataChanged = [];
this.result = 'not processed';
}
addError(error) {
if(error) {
console.error(error);
this.errors = this.errors.concat(error);
}
}
addDataChanged(dataChanged) {
if(dataChanged) {
this.dataChanged = this.dataChanged.concat(dataChanged);
}
}
setResult() {
if(this.errors.length) {
if(this.dataChanged.length) {
this.result = 'succeeded, but with complications';
} else {
this.result = 'failed';
}
} else {
if(this.dataChange.length) {
this.result = 'success';
}
}
}
}
This Class, named ProcessStateManagement is meant to be instantiated at the start of a complex process. We instantiate it with the dataToProcess attribute, which is intended to be the data we are running through said process. The constructor creates an object with four attributes: sizeOfDataToProcess, errors, dataChanged, and result. The mocked sizeOfData() function inside ProcessStateManagement’s constructor is meant to communicate what size of data we are processing as a meta reference. This populated (Number data type) into the sizeOfDataToProcess attribute. At instantiation, the errors and dataChanged attributes are both empty arrays and result is the string ‘not processed’. There are three methods in ProcessStateManagement: addError(), which will log and append an error to the errors attribute array; addDataChanged() which appends a dataChanged reference to the dataChanged array, and setResult(), which will set the value of the result attribute to whatever we send it. So, we have the four attributes, one set at instantiation and the others able to be added to or adjusted as we go through a process by the three methods. This is both a typical and simplified example of real state management classes that I have recently created.
These type of state management tools have been very helpful, helping me to keep process operation and results in order and to construct things like process reports. The problem I had was that they were getting wordy. For instance, there are real cases where a process will return a success, an unrelated but relevant error, and since it’s at the end of the process I need to register both AND set the result of my overall process (which typically is either ‘success’, ‘failed’ or ‘succeeded, but with complications’ (the partial success case). Such a scenario could end up looking something like this:
function someProcess(data) {
let PSM = new ProcessStateManagement(data);
const { errors, dataChanged } = doSomeProcess(data);
PSM.addError(errors);//any errors would be appended here
PSM.addDataChanged(dataChanged);//and dataChanged would be appended here
PSM.setResult();//determines the result based on the errors & dataChanged state
return PSM;//using the stateManagement as a report style object data set}
}
This may appear to be something that would rarely happen, but I’ve encountered scenarios like this a surprising number of times. I love what the methods are doing in updating the state and handling things like logging implicitly. However, I’d like to do this a bit less wordy. After some research, I found that one of the benefits of using Class as a constructing tool is that we can return this (self) from any method. This allows us to reflexively use the Class methods in a chained way. Here’s how this feature changes the Class itself:
class ProcessStateManagement {
constructor(dataToProcess) {
this.sizeOfDataToProcesss = (dataToProcess) ? sizeOfData(dataToProcess) : 0;
this.errors = [];
this.dataChanged = [];
this.result = 'not processed';
}
addError(error) {
if(error) {
console.error(error);
this.errors = this.errors.concat(error);
}
return this;
}
addDataChanged(dataChanged) {
if(dataChanged) {
this.dataChanged = this.dataChanged.concat(dataChanged);
}
return this;
}
setResult() {
if(this.errors.length) {
if(this.dataChanged.length) {
this.result = 'succeeded, but with complications';
} else {
this.result = 'failed';
}
} else {
if(this.dataChange.length) {
this.result = 'success';
}
}
return this;
}
}
At the end of every method, instead of implicitly returning undefined (as all functions do implicitly), I explicitly return this (self, the Class instance). This allows us to be able to chain the Class methods to our content. Here’s the same someProcess() code with these changes:
function someProcess(data) {
let PSM = new ProcessStateManagement(data);
const { errors, dataChanged } = doSomeProcess(data);
return PSM.addError(errors).addDataChanged(dataChanged).setResult();//in order, all on one line, AND I can return it!
}
As you can see, my state management has been reduced by a number of lines, and since the methods and variables read nicely left-to-right, we can easily determine what happens. The bonus is that since we are returning this (self) from the last method, we can return the entire line as our resulting value from someProcess().
Simple patterns like this have been very beneficial in helping me manage complex processes in straightforward and somewhat simplified ways. I really appreciate the clarity and simplicity of the Class declarative structure and the chainable nature of its methods (when used in this manner) for my process state management needs.