Sitemap

Understanding SOLID in Javascript

4 min readJul 17, 2023

--

SOLID is an acronym for five principles of object-oriented programming and design that provide a set of guidelines for writing clean, maintainable, and scalable code. These principles were first introduced by Robert C. Martin and have since become a cornerstone of software development practices. In this article, we will explore each SOLID principle and provide examples of how they can be applied in JavaScript.

Press enter or click to view image in full size

Single Responsibility Principle (SRP):

The Single Responsibility Principle states that a class or module should have only one reason to change. In other words, it should have a single responsibility. By adhering to this principle, we ensure that each class or module focuses on doing one thing and doing it well.

Example: Let’s say we have a User class that handles both user authentication and user data manipulation. This violates the SRP because these are two separate responsibilities. Instead, we can split it into two separate classes: AuthenticationService and UserDataService. Now, each class has a single responsibility, making the code easier to understand and maintain.

Open/Closed Principle (OCP):

The Open/Closed Principle states that classes, modules, or functions should be open for extension but closed for modification. In other words, we should be able to extend the behavior of a class without modifying its existing code.

Example: Suppose we have a Shape class with a calculateArea method. To add a new shape, instead of modifying the Shape class, we can create a new class that extends Shape and overrides the calculateArea method. This way, we adhere to the OCP, as we extend the behavior without modifying the existing code.

class Shape {
calculateArea() {
// common area calculation logic
}
}

class Rectangle extends Shape {
calculateArea() {
// specific area calculation logic for Rectangle
}
}

class Circle extends Shape {
calculateArea() {
// specific area calculation logic for Circle
}
}

Liskov Substitution Principle (LSP):

The Liskov Substitution Principle states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program. In simpler terms, any child class should be able to substitute its parent class without causing any issues. Lets understand this with an example:

class Bird {
fly() {
console.log("Bird is flying...");
}
}

class Sparrow extends Bird {
fly() {
console.log("Sparrow is flying...");
}
}

class Penguin extends Bird {
// Penguins cannot fly, so we omit the fly method
}

function makeBirdFly(bird) {
bird.fly();
}

const bird1 = new Sparrow();
const bird2 = new Penguin();

makeBirdFly(bird1); // Output: "Sparrow is flying..."
makeBirdFly(bird2); // Error or Unexpected Behavior

In this example, we have a Bird class and two subclasses: Sparrow and Penguin. Both Sparrow and Penguin are birds, but they have different capabilities. The fly method is overridden in both subclasses to provide specialized behavior.

Get Prerna Gupta’s stories in your inbox

Join Medium for free to get updates from this writer.

The issue arises when we try to apply the Liskov Substitution Principle by substituting a Penguin object (subclass) for a Bird object (superclass) in the makeBirdFly function. Since penguins cannot fly, this violates the LSP, leading to unexpected behavior or errors. In this case, the code will likely throw an error when trying to call the fly method on a Penguin object.

Interface Segregation Principle (ISP):

The Interface Segregation Principle states that clients should not be forced to depend on interfaces they do not use. In other words, it’s better to have smaller, more specific interfaces rather than a single large interface.

Example: Suppose we have an interface called Shape with methods like calculateArea, calculatePerimeter, and draw. However, some objects might only need to calculate the area and not the perimeter or vice versa. In such cases, we can split the Shape interface into smaller interfaces like AreaCalculable and PerimeterCalculable, allowing clients to depend only on the interfaces they need.

// Bad design
interface Shape {
calculateArea();
calculatePerimeter();
draw();
}

// Good design
interface AreaCalculable {
calculateArea();
}

interface PerimeterCalculable {
calculatePerimeter();
}

class Rectangle implements AreaCalculable, PerimeterCalculable {
// implement specific interfaces
}

Dependency Inversion Principle (DIP):

The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Instead, both should depend on abstractions. It also emphasizes that abstractions should not depend on details; details should depend on abstractions.

Example: Suppose we have a UserService that directly depends on a concrete DatabaseService to retrieve user data. This violates the DIP because the high-level module (UserService) depends on a low-level module (DatabaseService). Instead, we can introduce an abstraction, such as a UserDataRepository interface, that both UserService and DatabaseService can depend on. This way, the dependency is inverted, and the code becomes more flexible and testable.

class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}

getUsers() {
return this.userRepository.getUsers();
}
}

class DatabaseService {
getUsers() {
// fetch users from the database
}
}

// Dependency inversion using an interface
interface UserDataRepository {
getUsers();
}

class DatabaseRepository implements UserDataRepository {
getUsers() {
// fetch users from the database
}
}

const userRepository = new DatabaseRepository();
const userService = new UserService(userRepository);

Understanding and applying SOLID principles in JavaScript can greatly improve the quality of your code. By following these principles, you can create code that is more modular, maintainable, and adaptable to changes. Incorporate these principles into your development workflow, and you’ll see the benefits in the long run. Happy coding!

--

--

Prerna Gupta
Prerna Gupta

Written by Prerna Gupta

Technical Architect @sourcefuse