Are You Solid with SOLID? (Part 1) — Single Responsibility Principle
Let’s take a stab at making sense of SOLID — and why it really matters to a software engineer.
Many developers know that SOLID is an acronym, but fewer truly apply it in their code. In this post, instead of memorizing definitions, we’ll practice SOLID — starting with the 'S' (Single Responsibility Principle) — by refactoring a tiny calculator app.
Knowing what it stands for is good while communicating with fellow developer(s) or perhaps answering to ‘what is SOLID’ to an interviewer but the essence is always in comprehension.
So lets start with a tiny project to comprehend SOLID
Requirement- Build an application that can do basic mathematical calculations such as addition, subtraction, multiplication, and division.
I would be using JavaScript for this but remember language is just a tool to build something and it’s what is built that really matters.
Let’s get on with it
Task 1 - Take input(operands) and the operation from the user.
Task 2 - Perform the operation on the given operands to calculate the result.
Task 3 - Provide the answer and wait for another input.
Pretty simple right? Let’s implement our calculator.
Version One (v1)
Create a file named calculator.js
Add the code below that implements the basic mathematical calculation
import readline from "readline";
//This function does everything for us
function processExpression() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
//Ask for input
rl.question("Enter an expression (e.g., 3+4): ", (input) => {
// Parse the input
let operator, parts;
if (input.includes("+")) {
operator = "ADDITION";
parts = input.split("+");
} else if (input.includes("-")) {
operator = "SUBTRACTION";
parts = input.split("-");
} else if (input.includes("*")) {
operator = "MULTIPLICATION";
parts = input.split("*");
} else if (input.includes("/")) {
operator = "DIVISION";
parts = input.split("/");
} else {
console.log("Unknown operator");
rl.close();
return;
}
// get the numbers to perform operation on
const left = parseFloat(parts[0].trim());
const right = parseFloat(parts[1].trim());
// Evaluate the exression
let result;
switch (operator) {
case "ADDITION":
result = left + right;
break;
case "SUBTRACTION":
result = left - right;
break;
case "MULTIPLICATION":
result = left * right;
break;
case "DIVISION":
result = right !== 0 ? left / right : NaN;
break;
}
// Finally display the result
console.log(`${input} = ${result}`);
rl.close();
});
}
//Start our all in our function
processExpression();Call the function using node
node ./calculator.jsand you should see calculator in action as shown below
It does what it is supposed to do i.e basic math on two operands.
At first glance, this works fine. But look closer: one function is reading input, parsing it, performing the calculation, and printing the result. That’s four responsibilities in a single function.Here is how the modified code looks like
Version two (v2)
Update existing calculator.js or create a new calculator.js and rename the previous one as calculator_v1.js for comparing.
import readline from "readline";
/**
*
* This reads the input from std input (keyboard) and passess that input to the
* passed callback function for processing.
*/
function getUserInput(callback) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question("Enter an expression (e.g., 3+4): ", (input) => {
rl.close();
callback(input);
});
}
/**
* Parses the input that is read by the getUserInput.
* It returns an object
* {
* left: number,
* right : number,
* operator: "ADDITION" or "SUBTRACTION" or "MULTIPLICATION" or "DIVISION"
* }
*
*/
function parseExpression(input) {
if (input.includes("+"))
return {
left: input.split("+")[0],
right: input.split("+")[1],
operator: "ADDITION",
};
if (input.includes("-"))
return {
left: input.split("-")[0],
right: input.split("-")[1],
operator: "SUBTRACTION",
};
if (input.includes("*"))
return {
left: input.split("*")[0],
right: input.split("*")[1],
operator: "MULTIPLICATION",
};
if (input.includes("/"))
return {
left: input.split("/")[0],
right: input.split("/")[1],
operator: "DIVISION",
};
return { operator: "UNKNOWN" };
}
/**
*
* Evaluate the parsed expresson and return the result.
*/
function evaluateExpression(parsed) {
const left = parseFloat(parsed.left?.trim());
const right = parseFloat(parsed.right?.trim());
switch (parsed.operator) {
case "ADDITION":
return left + right;
case "SUBTRACTION":
return left - right;
case "MULTIPLICATION":
return left * right;
case "DIVISION":
return right !== 0 ? left / right : NaN;
default:
return NaN;
}
}
/**
*
* Just print the given input and the result of the evaluated expression.
*/
function displayResult(input, result) {
console.log(`${input} = ${result}`);
}
// Orchestrator: wires everything together
function processExpression(input) {
const parsed = parseExpression(input);
const result = evaluateExpression(parsed);
displayResult(input, result);
}
/**
* This is the entry point.
* This calls the getUserInput function and passes it the callback which will process that input.
**/
getUserInput(processExpression);we run it the same way i.e
node ./calculator.jsand the result is same, as expected
Enter an expression (e.g., 3+4): 7*8
7*8 = 56The S in SOLID stands for Single Responsibility Principle(SRP). It tells us that a function or class should do just one thing — no more, no less.
The Single Responsibility Principle is less about rules and more about perspective. It’s a way of looking at code and asking: Does this piece of code do only one job?
As you saw, the output of our calculator didn’t change — but the clarity and maintainability improved drastically. That’s the essence of SRP . And this mindset will guide us as we move through the rest of SOLID in upcoming posts.
Thanks for reading. Keep practicing — with time, applying these principles will feel like second nature.



