Logo de Grรฉgory Leroux

Grรฉgory Leroux

  • ๐…๐จ๐ฎ๐ง๐๐š๐ญ๐ข๐จ๐ง๐ฌ ๐จ๐Ÿ ๐Ž๐›๐ฃ๐ž๐œ๐ญ-๐Ž๐ซ๐ข๐ž๐ง๐ญ๐ž๐ ๐๐ซ๐จ๐ ๐ซ๐š๐ฆ๐ฆ๐ข๐ง๐ 

    solid image

    1. ๐’๐ข๐ง๐ ๐ฅ๐ž ๐‘๐ž๐ฌ๐ฉ๐จ๐ง๐ฌ๐ข๐›๐ข๐ฅ๐ข๐ญ๐ฒ ๐๐ซ๐ข๐ง๐œ๐ข๐ฉ๐ฅ๐ž (๐’๐‘๐):

    Before SRP :

    class User {
      constructor(name,email) {
        this.name = name;
        this.email = email;
      }
    
      getUserInfo() {
        return `Name: ${this.name}, Email: ${this.email}`;
      }
    
      saveUserToDatabase() {
        // Code to save user to database
        console.log('User saved to DB.');
      }
    }
    

    After SRP :

    class User {
      constructor(name,email) {
        this.name = name;
        this.email = email;
      }
    
      getUserInfo() {
        return `Name: ${this.name}, Email: ${this.email}`;
      }
    }
    
    class UserRepository {
      save(User) {
        console.log('User saved to DB.');
      }
    }
    
    const user = new User('John Doe', 'johndoe@example.com');
    const userRepository = new UserRepository();
    userRepository.save(user);   
    

    2. ๐Ž๐ฉ๐ž๐ง/๐‚๐ฅ๐จ๐ฌ๐ž๐ ๐๐ซ๐ข๐ง๐œ๐ข๐ฉ๐ฅ๐ž (๐Ž๐‚๐):

    Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.

    Before ๐Ž๐‚๐ :

    class Rectangle {
      constructor(width, height) {
        this.width = width;
        this.height = height;
      } 
    }  
    
    class Circle { 
      constructor(radius) {
        this.radius = radius;
      }
    } 
    
    function calculateArea(shape) {
      if (shape instanceof Rectangle) {
        return shape.width * shape.height;
      } else if (shape instanceof Circle) { 
        return Math.PI * shape.radtus * shape.radius;
      }
      // Imagine adding more shapes here would require modifying this function 
    

    After ๐Ž๐‚๐ :

    class Shape { 
      area() { 
        throw new Error("This method must be ovirridden!"); 
      } 
    
    class Rectangle extends Shape { 
      constructor(width, height) {
        super();
        this.width = width;
        this.height = height;
      } 
      
      area() {
        return this.width * this.height; 
      }
    } 
    
    class Circle extends Shape { 
      constructor(radius) {
        super();
        this.radius = radius;
      } 
      
      area() {
        return Math.PI * this.radius * this.radius;
      }
    }
    
    const shapes = [
      new Rectangle(10, 20),
      new Circle(5),
    ]; 
    

    3. ๐‹๐ข๐ฌ๐ค๐จ๐ฏ ๐’๐ฎ๐›๐ฌ๐ญ๐ข๐ญ๐ฎ๐ญ๐ข๐จ๐ง ๐๐ซ๐ข๐ง๐œ๐ข๐ฉ๐ฅ๐ž (๐‹๐’๐):

    Objects of a derived class should be able to replace objects of the base class without altering the correctness of the program.

    Before ๐‹๐’๐ :

    class Bird {
      fly() { 
        console.log('Flying');
      } 
    } 
    
    class Ostrich extends Bird {
      fly() { 
        throw new Error('Ostriches cannot fly'); 
      }
    }
    
    function makeBirdFly(bird) {
      bird.fly(); 
    } 
    
    const ostrich = new Ostrich(); 
    makeBirdFly(ostrich); // Error: Ostriches cannot fly 
    

    After ๐‹๐’๐ :

    class Bird { 
      move() { 
        console.log('Moving'); 
      } 
    }
    
    class FlyingBird extends Bird {
      fly() { 
        console.log('Flying'); 
      } 
    } 
    
    class Ostrich extends Bird {
      move() { 
        console.log('Running'); 
      } 
    } 
    
    function makeBirdMove(bird) { 
      bird.move();
    } 
    
    const ostrich = new Ostrich(); 
    makeBirdMove(ostrich); //Running 
    

    4. ๐ˆ๐ง๐ญ๐ž๐ซ๐Ÿ๐š๐œ๐ž ๐’๐ž๐ ๐ซ๐ž๐ ๐š๐ญ๐ข๐จ๐ง ๐๐ซ๐ข๐ง๐œ๐ข๐ฉ๐ฅ๐ž (๐ˆ๐’๐):

    Clients should not be forced to depend on interfaces they do not use.

    Before ISP :

    class Worker { 
      construct(name) { 
        this.name = name; 
      } 
    }
    
    work() {
      console.log(`${this.name} is working`)
    }
    
    eat() {
      console.log(`${this.name} is eating`)
    }
    
    class Robot extends Worker {
      eat() {
        throw new Error('Robots do not eat');
      }
    }
    
    const robot = new Robot('robo');
    robot.eat(); // Error: Robots do not eat
    

    After ISP :

    class Worker { 
      construct(name) { 
        this.name = name; 
      } 
    }
    
    class HumanWorker extends Worker { 
      work() {
        console.log(`${this.name} is working`)
      }
    
      eat() {
        console.log(`${this.name} is eating`)
      }
    }
    
    class Robot extends Worker {
      work() {
        console.log(`${this.name} is working`)
      }
    }
    
    const robot = new Robot('robo');
    robot.work(); // Robot is working
    

    5. ๐ƒ๐ž๐ฉ๐ž๐ง๐๐ž๐ง๐œ๐ฒ ๐ˆ๐ง๐ฏ๐ž๐ซ๐ฌ๐ข๐จ๐ง ๐๐ซ๐ข๐ง๐œ๐ข๐ฉ๐ฅ๐ž (๐ƒ๐ˆ๐):

    High-level modules should not depend on low-level modules. Both should depend on abstractions.

    Before DIP :

    The Notification class is directly coupled to the EmailService class. If you want to use another type of notification service (such as SMS), you need to modify the Notification class.

    class EmailService {
      send(message) {
        console.log(`Sending email: ${message}`)
      }
    }
    
    class Notification {
      constructor() {
        this.emailService = new EmailService();
      }
    
      sendNotification(message) {
        this.emailService.send(message);
      }
    }
    
    const notification = new Notification();
    notification.sendNotification('Hello, this is a notification');
    

    After DIP :

    The Notification class depends on the abstraction NotificationService instead of concrete implementations. You can inject any implementation of NotificationService (such as EmailService or SMSService) without modifying the Notification class. This makes the code more flexible and extensible.

    // Abstraction (interface)
    class NotificationService {
      send(message) {
        throw new Error('This method must be overriden!')
      }
    }
    
    class EmailService extends NotificationService {
      send(message) {
        console.log(`Sending email: ${message}`)
      }
    }
    
    class SMSService extends NotificationService {
      send(message) {
        console.log(`Sending SMS: ${message}`)
      }
    }
    
    class Notification {
      constructor(notificationService) {
        this.notificationService = notificationService();
      }
    
      sendNotification(message) {
        this.notificationService.send(message);
      }
    }
    }
    
    const emailService = new EmailService();
    const smsService = new SMSService();
    
    const emailNotification = new Notification(emailService);
    emailNotification.sendNotification('Hello, this is an email notification');
    
    const smsNotification = new Notification(smsService);
    smsNotification.sendNotification('Hello, this is an SMS notification');