Refactoring a video player using reveal module and command pattern in JS
So, the initial code is the following:
The result of our code is the shown in the following image.
USE INTERNATIONAL LANGUAGE TO CODE: ENGLISH + PRETTIER
The first step in any code of this world is use the international language of coding. Today the mainstream language is English, I know that the other languages such as Chinese or Spanish are top trending language but really is more easy understand a code which is written in English. I’m Spanish-native and for me is more difficult write and speak in English but I know that if I write my code in English I can share my code with the world.
So, the first step is translate my code to English, It is not required a perfect English because the majority of the audience is not English-native.
Another important and basic tip to code is use a tool which normalize your code because there is not relevant the spaces or tabs in your code, the number of characters which use in each line or the way in that your create a new object but there is very important that you always do this task the same way. I.e, you need normalize your code with you and your partners. The best tool in the Web develop ecosystem today es Prettier. So, I recommend you that you install this tool in your favorite IDE (in my case I’m using the extension in VSCode) .
Now, you can read the code using an international language and using a tool which formatted my code:
The GitHub project in this branch is https://github.com/Caballerog/refactoring-js-patterns-tips/tree/01-international-code.
VARIABLE SCOPE — REVEAL MODULE PATTERN
There is very important that you know how variable scope works in your language because you can avoid effects side in the future and to do your code more cleaner and readable.
- Only write your variable in any place of your code. This is the worst way to declare a variable. In this case, the variable has a scope GLOBAL in your app (OMG!! since a private function are you defining a GLOBAL variable) which can provoke side-effects and several conflicts in your code. So, never declare a variable without a keyword.
- Use the keyword var. Using the keyword var you have hoisting and functional scope. Your variable can provoke side-effects inside a function because the variable is define in the top of your code (in your function) and there is in any place inside your function and the value is assigned in the line of code in which you are initializing your variable.
- Use the keyword let/const. This is the way in which ES6 and the community prefer used to define the variables because the scope of this variables is block (as you expect it to behave because most languages using a block scope) but the main different between let and const is that the pointer of memory in which is store the reference of the value of the variable can change or not (OMG!). In other words, let is used to variables in which the value can change in the code and const is used when the value of the variable can not change if the type is primitive (number or string) and can the pointer of the value can not change if the type is not primitive, for example objects and array.
The global variables that was define in the init function are moved to the top of the script and use the keyword
const because of there are references to the DOM or a primitive value such as
max. Also, there is a variable called
loop which is used to store the reference ID using returned in the
At the moment, both code are the same behavior because the variables were global and in this moment are global too. For this reason, we could use the pattern Reveal Module which allow create private scope to the functions and variables. This pattern can be read in this blogpost.
The pattern module reveal allow us convert in public our interface of communication with the other pieces of the code. In this case the methods
move. The method
state now has the scope of our module which is called
player. The rest of variables are private too, so there, we just to avoid side-effects provoke for the global scope of our variables or methods.
The code with applying this pattern is the following:
But the code below there is not works due to, the pattern module reveal is using a IIFE which run before that the DOM is loaded, so the variables
play, etc. are
nullwhen the script run for these lines. The solution is move the definition of this variables inside the method init, but in this refactoring we going to create a object called
GUIwhich store the information about the DOM (in large apps this object would be translate to other files using the famous pattern MVC.
So, the final code in this step is the following:
The GitHub project in this branch is hhttps://github.com/Caballerog/refactoring-js-patterns-tips/tree/02-scope-module.
CREATE CONDITIONAL METHODS
The following step to avoid complex in our code consist in create functions which abstract your complex logic in simple and readable functions to take decision about of different states.
For example, the logic from the conditional sentence can be extract in short functions using the prefix “is” to express that the function return a boolean value.
The code could be the following:
The GitHub project in this branch is https://github.com/Caballerog/refactoring-js-patterns-tips/tree/04-create-conditional-methods.
The next step can be extract code in readable pieces of code (functions) which add semantic value in our code. Thanks to this decision we can think about the goal of our application. So, the method click and state can be refactor in the following methods:
The GitHub project in this branch is https://github.com/Caballerog/refactoring-js-patterns-tips/tree/05-short-methods.
In this moment, the code is a bit confuse because we are using if-else to use a function which to do a action or command. There is a classical pattern called
So, the first step is define the commands/actions, to simply the code, this object is define in the same player.js.
The actions are Play, Pause, State end and State Playing. The command pattern consist in execute a method or action using this key (in our example there is not required parameters), therefor the command object is the following:
Now, the moment in which the if-else dissapers using the pattern command:
The GitHub project in this branch is https://github.com/Caballerog/refactoring-js-patterns-tips/tree/06-command-pattern.
In this example I’ve done several changes to improve the quality of my code. The quality of my code is not relational with performance but also about the readable and extensible. The following list is a resume about the different techniques which I’ve been applied in my code.
- International Code. Never programming using your local language, unless your local language is English. Nowadays, the international language is the english. So, you must develop your code using english. Imagine a code in German or French or Polish and you don’t know that language!
- Prettier + CommitLint
- Create conditional methods. Refactor your condition in functions which abstract your complex logic in a simple and readable function.
- Create short, simple and readable functions. Extract code in reusable functions or, at least, add semantic value in your code.
The GitHub project is https://github.com/Caballerog/refactoring-js-patterns-tips.
Originally published at www.carloscaballero.io on January 11, 2019.