SOLID 原则在 JavaScript 中的介绍
SOLID 是由 Robert Martin(鲍勃大叔)提出的面向对象编程的五个基本原则的首字母缩写。尽管 JavaScript 最初并不是一种严格类型的 OOP 语言,但现代 JavaScript(ES6+)通过类、模块和 TypeScript 积极运用这些原则来构建可扩展且可维护的应用程序,尤其是在 Node.js 环境中。
在本文中,我们将通过 JavaScript 的实际代码示例逐一解析 SOLID 的五个原则。理解这些原则将帮助您编写干净、灵活且易于应对变更的代码。
1. Single Responsibility Principle (SRP) — 单一职责原则
该原则指出:一个类或模块应该只有一个引起它变化的原因。换句话说,每个模块只负责一个特定的功能。
违反 SRP
考虑一个同时管理用户、发送邮件和记录操作的 UserService 类:
class UserService { constructor() { this.users = []; }
addUser(name) { this.users.push({ name }); console.log('User added'); // 日志记录 this.sendEmail(name); // 发送邮件 }
sendEmail(name) { console.log(`Email sent to ${name}`); }
getUsers() { return this.users; }}这里,该类承担了三个职责:数据管理、日志记录和通知发送。修改日志逻辑或邮件发送逻辑都会导致 UserService 类发生变化。
遵循 SRP
将职责分离到不同的类中:
class Logger { log(message) { console.log(`[LOG]: ${message}`); }}
class EmailService { send(name) { console.log(`Email sent to ${name}`); }}
class UserService { constructor(logger, emailService) { this.users = []; this.logger = logger; this.emailService = emailService; }
addUser(name) { this.users.push({ name }); this.logger.log(`User ${name} added`); this.emailService.send(name); }
getUsers() { return this.users; }}现在每个类只负责自己的任务,一个服务中的变更不会影响其他服务。
2. Open-Closed Principle (OCP) — 开闭原则
该原则指出:软件实体应该对扩展开放,对修改关闭。也就是说,我们应该能够添加新功能,而无需修改现有代码。
违反 OCP
假设我们有一个计算图形面积的函数:
function calculateArea(shape) { if (shape.type === 'circle') { return Math.PI * shape.radius ** 2; } else if (shape.type === 'rectangle') { return shape.width * shape.height; } // 添加新图形时必须修改此函数}遵循 OCP
通过类使用多态性:
class Shape { area() { throw new Error('Method "area()" must be implemented'); }}
class Circle extends Shape { constructor(radius) { super(); this.radius = radius; } area() { return Math.PI * this.radius ** 2; }}
class Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } area() { return this.width * this.height; }}
// 添加新图形无需修改现有代码class Triangle extends Shape { constructor(base, height) { super(); this.base = base; this.height = height; } area() { return (this.base * this.height) / 2; }}
function calculateArea