引言:为什么 SOLID 对 TypeScript 开发者很重要
SOLID 是 Robert Martin(鲍勃大叔)提出的面向对象编程的五个基本原则。在结合了静态类型和 JavaScript 灵活性的 TypeScript 中,应用 SOLID 原则可以创建可扩展、可测试且易于维护的应用程序。在本文中,我们将从理论基础和 TypeScript 实践示例两方面来剖析每个原则。
1. 单一职责原则 (SRP)
理论
一个类应该只有一个引起变化的原因。这意味着每个模块、类或函数只负责一项功能。
TypeScript 实践
考虑一个违反 SRP 的类,它同时管理用户和发送通知:
// ❌ 违反 SRPclass UserManager { constructor(private name: string, private email: string) {}
saveUser(): void { console.log(`用户 ${this.name} 已保存到数据库`); }
sendWelcomeEmail(): void { console.log(`欢迎邮件已发送至 ${this.email}`); }}分离职责后的修正版本:
// ✅ 遵守 SRPclass User { constructor(public name: string, public email: string) {}}
class UserRepository { save(user: User): void { console.log(`用户 ${user.name} 已保存到数据库`); }}
class EmailService { sendWelcomeEmail(user: User): void { console.log(`欢迎邮件已发送至 ${user.email}`); }}2. 开闭原则 (OCP)
理论
软件实体应对扩展开放,对修改关闭。换句话说,您应该能够在不修改现有代码的情况下添加新功能。
TypeScript 实践
针对不同客户类型计算折扣的示例:
// ❌ 违反 OCPclass DiscountCalculator { calculateDiscount(customerType: string, amount: number): number { if (customerType === 'regular') { return amount * 0.1; } else if (customerType === 'premium') { return amount * 0.2; } else if (customerType === 'vip') { return amount * 0.3; } return 0; }}// ✅ 通过多态遵守 OCPinterface DiscountStrategy { calculate(amount: number): number;}
class RegularDiscount implements DiscountStrategy { calculate(amount: number): number { return amount * 0.1; }}
class PremiumDiscount implements DiscountStrategy { calculate(amount: number): number { return amount * 0.2; }}
class DiscountCalculator { constructor(private strategy: DiscountStrategy) {}
calculate(amount: number): number { return this.strategy.calculate(amount); }}3. 里氏替换原则 (LSP)
理论
程序中的对象应该可以被其子类型的实例替换,而不影响程序的正确性。简单来说:如果你有一个基类,它的任何子类都应该能在使用基类的任何地方正常工作。
TypeScript 实践
几何图形的经典示例:
// ❌ 违反 LSPclass Rectangle { constructor(protected width: number, protected height: number) {}
setWidth(width: number): void { this.width = width; }
setHeight(height: number): void { this.height = height; }
getArea(): number { return this.width * this.height; }}
class Square extends Rectangle { setWidth(width: number): void { this.width = width; this.height = width; // 违反原则! }
setHeight(height: number): vo