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 原则很重要 🚀
- 更容易维护:更改影响的组件更少。
- 更好的可测试性:隔离的逻辑更容易测试。
- 灵活的架构:无需重写即可适应新需求。
- 可重用性:组件可以在不同项目中重用。