SOLID Principle Java Script
Sunil Prajapati
Posted on November 8, 2024
The SOLID principles are a set of five design principles in object-oriented programming that help developers write more maintainable, scalable, and robust code. These principles were introduced by Robert C. Martin (also known as "Uncle Bob") and form the foundation for writing clean and well-structured code.
Here's a breakdown of each principle:
1. S - Single Responsibility Principle (SRP)
Definition:
A class should have only one reason to change, meaning it should have only one job or responsibility.
Explanation:
- Each class should focus on a single task or functionality.
- If a class has more than one responsibility, changes to one responsibility can impact the other, making the class harder to maintain.
Example:
// BAD: Class with multiple responsibilities
class UserProfile {
constructor(user) {
this.user = user;
}
// Responsibility 1: Fetch user data
getUserData() {
// Fetch user data logic
}
// Responsibility 2: Render user profile
renderProfile() {
// Render profile logic
}
}
// GOOD: Single Responsibility
class UserDataService {
getUserData() {
// Fetch user data logic
}
}
class UserProfileRenderer {
renderProfile(user) {
// Render profile logic
}
}
2. O - Open/Closed Principle (OCP)
Definition:
Software entities (classes, modules, functions) should be open for extension but closed for modification.
Explanation:
- You should be able to add new functionality without changing existing code.
- This is often achieved through abstraction (e.g., interfaces, abstract classes) and polymorphism.
Example:
// BAD: Modifying existing class to add new functionality
class Discount {
getDiscount(type) {
if (type === 'regular') return 5;
else if (type === 'premium') return 10;
}
}
// GOOD: Open for extension, closed for modification
class Discount {
getDiscount() {
return 0;
}
}
class RegularDiscount extends Discount {
getDiscount() {
return 5;
}
}
class PremiumDiscount extends Discount {
getDiscount() {
return 10;
}
}
3. L - Liskov Substitution Principle (LSP)
Definition:
Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.
Explanation:
- Subclasses should extend the behavior of the superclass without changing its expected behavior.
- This principle is violated if a subclass changes the behavior in a way that breaks the functionality of the parent class.
Example:
// BAD: Subclass violating Liskov Substitution Principle
class Bird {
fly() {
console.log('Flying');
}
}
class Penguin extends Bird {
fly() {
throw new Error('Penguins cannot fly');
}
}
// GOOD: Adhering to Liskov Substitution Principle
class Bird {
move() {
console.log('Moving');
}
}
class FlyingBird extends Bird {
fly() {
console.log('Flying');
}
}
class Penguin extends Bird {
swim() {
console.log('Swimming');
}
}
4. I - Interface Segregation Principle (ISP)
Definition:
Clients should not be forced to depend on interfaces they do not use.
Explanation:
- Rather than having a single fat interface, you should split it into smaller, more specific interfaces.
- This prevents implementing classes from being forced to implement methods they don't need.
Example:
// BAD: Fat interface
class Machine {
start();
print();
scan();
fax();
}
class Printer implements Machine {
start() { }
print() { }
scan() { } // Not needed
fax() { } // Not needed
}
// GOOD: Segregated interfaces
class Printer {
print() { }
}
class Scanner {
scan() { }
}
class Fax {
fax() { }
}
5. D - Dependency Inversion Principle (DIP)
Definition:
High-level modules should not depend on low-level modules. Both should depend on abstractions. Also, abstractions should not depend on details. Details should depend on abstractions.
Explanation:
- This principle aims to reduce the coupling between high-level and low-level modules by introducing an abstraction layer.
- It helps to make the system more modular and easier to refactor or extend.
Example:
// BAD: High-level class depends on low-level class
class EmailService {
sendEmail(message) {
console.log(`Sending email: ${message}`);
}
}
class Notification {
constructor() {
this.emailService = new EmailService();
}
send(message) {
this.emailService.sendEmail(message);
}
}
// GOOD: Using Dependency Inversion
class EmailService {
send(message) {
console.log(`Sending email: ${message}`);
}
}
class Notification {
constructor(service) {
this.service = service;
}
send(message) {
this.service.send(message);
}
}
// Usage
const emailService = new EmailService();
const notification = new Notification(emailService);
notification.send('Hello, World!');
Summary of SOLID Principles
- SRP: A class should have only one responsibility.
- OCP: Classes should be open for extension but closed for modification.
- LSP: Subtypes should be substitutable for their base types.
- ISP: Split large interfaces into smaller, more specific ones.
- DIP: Depend on abstractions, not on concrete implementations.
By following these principles, you can write code that is easier to understand, maintain, and extend. These principles form the foundation of good software design and are crucial for developing scalable and robust systems.
Posted on November 8, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.