SOLID 原则在 JavaScript 中的实践:从理论到 Node.js 实现
SOLID 是由 Robert Martin(Uncle Bob)提出的五条面向对象设计基本原则。这些原则有助于创建灵活、可维护且可扩展的代码。尽管很多人将 SOLID 与经典的面向对象编程语言(如 Java、C#)联系在一起,但它们同样适用于 JavaScript,尤其是在 Node.js 开发中。本文将结合具体示例逐一剖析每条原则。
单一职责原则(SRP)
SRP 指出:"一个类(或模块)应该只有一个引起变化的原因"。这意味着每个模块只负责一项特定任务。
反面示例(违反 SRP):
class UserService { createUser(data) { /* 用户创建逻辑 */ } sendWelcomeEmail(email) { /* 发送邮件 */ } logActivity(action) { /* 日志记录 */ }}这里,一个类负责用户管理、发送邮件和日志记录。如果日志格式发生变化,就需要修改 UserService——尽管它的主要任务并没有改变。
正面示例(符合 SRP):
class UserRepository { createUser(data) { /* 仅处理数据库工作 */ }}
class EmailService { sendWelcomeEmail(email) { /* 仅负责发送 */ }}
class Logger { logActivity(action) { /* 仅负责记录 */ }}
class UserService { constructor(userRepo, emailService, logger) { this.userRepo = userRepo; this.emailService = emailService; this.logger = logger; } createUser(data) { const user = this.userRepo.createUser(data); this.emailService.sendWelcomeEmail(user.email); this.logger.logActivity('用户已创建'); return user; }}现在每个类只负责自身的职责范围。通过在 UserService 中组合它们,我们获得了灵活性和变更隔离性。
开闭原则(OCP)
OCP 指出:"软件实体应对扩展开放,对修改关闭"。也就是说,我们应该能够在不改动现有代码的情况下添加新功能。
反面示例(违反 OCP):
class PaymentProcessor { processPayment(type, amount) { if (type === 'card') { /* 信用卡处理 */ } else if (type === 'paypal') { /* PayPal 处理 */ } // 添加新支付方式时——需要修改此方法 }}正面示例(符合 OCP):
class PaymentStrategy { pay(amount) { throw new Error('必须实现此方法'); }}
class CardPayment extends PaymentStrategy { pay(amount) { console.log(`通过信用卡支付 ${amount}`); }}
class PayPalPayment extends PaymentStrategy { pay(amount) { console.log(`通过PayPal支付 ${amount}`); }}
class PaymentProcessor { constructor(strategy) { this.strategy = strategy; } processPayment(amount) { this.strategy.pay(amount); }}
// 使用示例const cardPayment = new PaymentProcessor(new CardPayment());cardPayment.processPayment(100);
const paypalPayment = new PaymentProcessor(new PayPalPayment());paypalPayment.processPayment(200);现在要添加新的支付方式,只需创建一个继承自 PaymentStrategy 的新类,并将其传递给 PaymentProcessor 即可。原始代码无需改动。
里氏替换原则(LSP)
LSP 指出:"程序中的对象应可被其子类型的实例替换,且不改变程序的正确性"。换句话说,如果你有一个基础