🛠️ JavaScript 中的 SOLID 原则

530 阅读2分钟

SOLID 原则是一套设计指南,帮助你创建可维护的代码。

让我们通过 JavaScript 示例来逐一解析这些原则:

1. 单一职责原则(SRP)

一个 类/函数 只有一个职责。

不好:

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  saveToDatabase() {
    // 数据库逻辑
  }

  sendEmail() {
    // 邮件逻辑
  }
}

问题: User 类同时处理数据管理和邮件逻辑。

好:

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

class UserRepository {
  saveToDatabase(user) { /* 数据库逻辑 */ }
}

class EmailService {
  sendEmail(user) { /* 邮件逻辑 */ }
}

好处: 每个类只有一个职责。

2. 开闭原则(OCP)

实体 应该对扩展开放,对修改关闭。

不好:

class Logger {
  logToConsole(message) {
    console.log(message);
  }

  logToFile(message) {
    // 写入文件
  }
}

// 要添加一个新的记录器(例如 HTTP),必须修改 Logger 类。

好:

// 使用策略模式扩展行为
class Logger {
  log(message, loggerStrategy) {
    loggerStrategy(message);
  }
}

// 定义策略(扩展)
const consoleStrategy = (msg) => console.log(msg);
const fileStrategy = (msg) => writeToFile(msg);
const httpStrategy = (msg) => fetch('/log', { body: msg }); // 添加新的记录器而无需修改 Logger 类

// 使用:
const logger = new Logger();
logger.log("Error!", httpStrategy); // 无需修改 Logger

好处: 无需修改现有代码即可扩展功能。

3. 里氏替换原则(LSP)

子类 应该能够替换其父类而不破坏功能。

不好:

class Rectangle {
  setWidth(w) { this.width = w }
  setHeight(h) { this.height = h }
}

class Square extends Rectangle {
  setSize(size) { // 违反 LSP
    this.width = size;
    this.height = size;
  }
}

function resizeShape(shape) {
  shape.setWidth(10);
  shape.setHeight(5); // 对于 Square 来说会出问题
}

好:

class Shape {
  area() { /* 抽象 */ }
}

class Rectangle extends Shape { /* 实现 area */ }
class Square extends Shape { /* 实现 area */ }

好处: 子类不会破坏父类的行为。

4. 接口隔离原则(ISP)

客户端 不应依赖于它们不使用的接口。

不好:

class Worker {
  work() { /* ... */ }
  eat() { /* ... */ }
}

// Robot 被迫实现 eat()
class Robot extends Worker {
  eat() { throw Error("Robots don't eat!"); }
}

好:

class Workable {
  work() { /* 接口 */ }
}

class Eatable {
  eat() { /* 接口 */ }
}

class Human implements Workable, Eatable { /* ... */ }
class Robot implements Workable { /* ... */ }

好处: 避免臃肿的接口。

5. 依赖倒置原则(DIP)

依赖于 抽象(接口),而不是 具体实现

不好:

class MySQLDatabase {
  save(data) { /* MySQL 特定 */ }
}

class UserService {
  constructor() {
    this.db = new MySQLDatabase(); // 紧耦合
  }
}

好:

class Database {
  save(data) { /* 抽象 */ }
}

class MySQLDatabase extends Database { /* ... */ }
class MongoDB extends Database { /* ... */ }

class UserService {
  constructor(database) {
    this.db = database; // 注入依赖
  }
}

好处: 解耦、可测试的代码。

为什么在 JavaScript 中 SOLID 原则很重要 🚀

  1. 更容易维护:更改影响的组件更少。
  2. 更好的可测试性:隔离的逻辑更容易测试。
  3. 灵活的架构:无需重写即可适应新需求。
  4. 可重用性:组件可以在不同项目中重用。

原文:dev.to/alaa-samy/s…