At some point in your project, you will need to write some algorithm. That algorithm doesn’t have to be a known one; it can be something specific to your project requirements.
For example, if you are building a table component, you might need to support columns re-ordering by dragging the column and moving it to the desired position. To make this work, you need to implement a way to detect the intersection between these columns.
To implement such a feature, you need to think about the way intersection works—for example, how much intersection there’s between the elements, and whether the intersection is happening from the left or the right.
There are many ways to implement the same algorithm. Some will be faster than others. Thinking about the performance of it and the best way to structure it will take time. But when you are in the early stages of that project, you need to make it work as soon as possible—maybe as a proof of concept or to show a quick prototype to someone.
The best thing you can do to overcome this issue is to implement the algorithm as quick as possible and then refactor it later.
But to be able to do that, you need to extract that algorithm into its own function. The extracted function has to be pure, which means it doesn’t cause any side effects to the rest of your code. It should be a function that takes a list of simple parameters (preferably, primitive values), runs the algorithm, and returns a result.
When it’s written this way, you can substitute that algorithm with a faster, cleaner one any time you want. And as long as it takes the same parameters and returns the same result, then it should work even if the way it computes that result is different—that’s the power of encapsulation and polymorphism.
Example
In this simple example, I have a list of posts that I want to sum their number of views to get the total number of views across all the published posts. Each post object has views
and published
fields. The views
field contains the number of views for that post, and the published
field is a boolean that shows whether the post is published or not.
Initially, I implemented this calculation code inside a function that returns some stats about the blog.
function blogStats() {
const posts = fetchAllPosts()
let totalNumberOfViews = 0
for (let i = 0; i < posts.length; i++) {
if (!posts[i].published) {
continue
}
totalNumberOfViews += posts[i].views
}
// ...
return {
totalViews: totalNumberOfViews
// other stats...
}
}
This is a very simple example; in real-world projects, your algorithms will likely be much longer and more complex. So let’s imagine it’s more complicated and I didn’t like its implementation when I initially wrote it.
To make it easy to refactor later, I need to extract it into its own function.
function getTotalPostViews(posts) {
let result = 0
for (let i = 0; i < posts.length; i++) {
if (!posts[i].published) {
continue
}
result += posts[i].views
}
return result
}
The caller code will use it like this:
function blogStats() {
const posts = fetchAllPosts()
let totalNumberOfViews = getTotalPostViews(posts)
// ...
return {
totalViews: totalNumberOfViews
// other stats...
}
}
Let’s say that I got back to it later, and I thought I can improve it by replacing loops with collection methods.
I can easily update it without worrying about changing something else the project.
function getTotalPostViews(posts) {
return posts
.filter((post) => post.published)
.map((post) => post.views)
.reduce((total, views) => total + views, 0)
}
As with any refactoring, I should have a test for it before changing it to make sure it’s still working.
Extracting the algorithm not only made it easier to refactor it later, but also introduced a new function that I can reuse later—so if I needed to get the total views in other part of my app, I would just call getTotalPostViews
and pass it the list of posts.