SOLID Principles in JavaScript: Theory and Practice from A to Z
\n\nDeveloping high-quality software requires not only knowledge of language syntax but also an understanding of architectural principles. SOLID is a set of five fundamental principles of object-oriented design that help create flexible, maintainable, and scalable code. In this article, we will break down each SOLID principle in the context of JavaScript (Node.js) with practical examples.
\n\nWhat is SOLID?
\n\nThe SOLID acronym was introduced by Robert Martin (Uncle Bob) and includes the following principles:
\n\n- \n
- S — Single Responsibility Principle \n
- O — Open/Closed Principle \n
- L — Liskov Substitution Principle \n
- I — Interface Segregation Principle \n
- D — Dependency Inversion Principle \n
These principles are universal and applicable to any programming language, including JavaScript. Let's look at each one in detail.
\n\n1. Single Responsibility Principle (SRP)
\n\nDefinition: A class or module should have only one reason to change. In other words, each module is responsible for one specific task.
\n\nBad example: A UserService class that handles saving a user, sending an email, and generating reports.
// ❌ SRP Violation\nclass UserService {\n saveUser(user) {\n // Save to database\n console.log('User saved');\n }\n\n sendEmail(user) {\n // Send email\n console.log('Email sent to ' + user.email);\n }\n\n generateReport(user) {\n // Generate report\n console.log('Report generated for ' + user.name);\n }\n}\n\nGood example: Split the responsibilities into separate classes.
\n\n// ✅ SRP Compliance\nclass UserRepository {\n save(user) {\n console.log('User saved');\n }\n}\n\nclass EmailService {\n send(user) {\n console.log('Email sent to ' + user.email);\n }\n}\n\nclass ReportGenerator {\n generate(user) {\n console.log('Report generated for ' + user.name);\n }\n}\n\n// Usage\nconst user = { name: 'Ivan', email: 'ivan@example.com' };\nconst userRepo = new UserRepository();\nconst emailService = new EmailService();\nconst reportGen = new ReportGenerator();\n\nuserRepo.save(user);\nemailService.send(user);\nreportGen.generate(user);\n\nNow each class has a single responsibility, and changes in one module do not affect others.
\n\n2. Open/Closed Principle (OCP)
\n\nDefinition: Software entities (classes, modules, functions) should be open for extension but closed for modification.
\n\nBad example: A function that calculates the area of shapes using conditional statements.
\n\n// ❌ OCP Violation\nfunction calculateArea(shape) {\n if (shape.type === 'circle') {\n return Math.PI * shape.radius ** 2;\n } else if (shape.type === 'rectangle') {\n return shape.width * shape.height;\n }\n // When adding a new shape, the function must be modified\n return 0;\n}\n\nGood example: Use polymorphism and inheritance.
\n\n// ✅ OCP Compliance\nclass Shape {\n area() {\n throw new Error('The area() method must be implemented');\n }\n}\n\nclass Circle extends Shape {\n constructor(radius) {\n super();\n this.radius = radius;\n }\n\n area() {\n return Math.PI * this.radius ** 2;\n }\n}\n\nclass Rectangle extends Shape {\n constructor