[第一部分] SOLID Typescript 简介
SOLID 原则是 OOP 中的一组设计原则,有助于创建稳健、可维护且可扩展的代码。在 Typescript 中实现它们非常简单,因为它支持类、接口和强类型。
在这篇文章中,我简要概述了 SOLID,以帮助您更轻松地理解它,并且我将在有时间时深入研究每个部分 _ =)))。
下面通过 Typescript 示例详细解释 SOLID 原则:
S——单一责任原则(SRP)
**一个类应该只有一个改变的原因**
这意味着每个类应该只做一件事。 如果需要做多件事,它应该将一些职责委托给其他类。
例如:
❌糟糕
class UserService { createUser(user: string){ console.log(`User ${user} created`); } // UserService sendWelcomeEmail(user: string){ console.log(`Welcome email sent to ${user}`) } //EmailService }
✅好
class UserService{ createUser(user: string): string { console.log(`User ${user} created`); return user; } } class EmailService{ sendMail(user: string){ console.log(`Send mail to ${user}`); } }
那么我应该如何在我的代码中正确有效地应用 SRP 呢?
=> **根据业务逻辑对职责进行分组。**
O——开放/封闭原则(OCP)
**软件实体应该对扩展开放,对修改关闭。**
这意味着您无需更改现有代码即可添加新功能。
❌糟糕
class DiscountService{ calculateDiscount(typeOfDiscount:string, amount: number) : number { if (typeOfDiscount == 'standard') { return amount * 0.1; } else if (typeOfDiscount == 'premium') { return amount * 0.5; } return 0; } } let standard = new DiscountService(); standard.calculateDiscount('standard', 10);
为什么它不好?
如果您收到添加 DiamondDiscount 的请求,要求其提供 100% 折扣以及“isMemberShip”等附加参数,则需要修改“calculateDiscount()”方法,如下所示:
class DiscountService{ calculateDiscount(typeOfDiscount:string, amount: number, isMemberShip: boolean) : number { if (typeOfDiscount == 'standard') { return amount * 0.1; } else if (typeOfDiscount == 'premium') { return amount * 0.5; } else if (typeOfDiscount == 'diamond') { if (isMemberShip){ return amount; } } return 0; } } let standard = new DiscountService(); standard.calculateDiscount('standard', 10); // => You must change this one if update calculateDiscount
这可能会影响已经在使用“calculateDiscount()”的其他方法(您需要搜索任何使用“calculateDiscount”的代码并将其更新为包含“isMemberShip”。
✅好
interface Discount { calculateDiscount(amount: number) : number; } class StandardDiscount implements Discount { calculateDiscount(amount: number): number { return amount * 0.1; } } class DiamondDiscount implements Discount { private isMemberShip: boolean = false; constructor(isMemberShip: boolean) { } calculateDiscount(amount: number): number { if (this.isMemberShip){ return amount; } return amount * 0.5; } } class DisCountService { constructor(private readonly discountStrategy: Discount) { } calculateDiscount(amount: number) { return this.discountStrategy.calculateDiscount(amount); } } const discount = new DisCountService( new StandardDiscount()); //Do not affect this code
所有问题均已通过 OCP 解决。
L——里氏替换原则(LSP)
**派生类必须可以替代它们的基类。**
这意味着你应该能够用子类替换父类而不会破坏功能
❌糟糕
class Rectangle { constructor(protected width: number, protected height: number) {} setWidth(width: number) { this.width = width; } setHeight(height: number) { this.height = height; } getArea(): number { return this.width * this.height; } } class Square extends Rectangle { setWidth(width: number) { this.width = width; this.height = width; // A square's height is always equal to its width } setHeight(height: number) { this.height = height; this.width = height; // A square's width is always equal to its height } } // Usage const shape: Rectangle = new Square(5, 5); shape.setWidth(10); console.log(shape.getArea()); // Expected: 50, Actual: 100
**为什么它不好?**
const shape: Rectangle = new Square(5, 5); //Substituting a `Square` for a `Rectangle` lead to incorrect behavior shape.setWidth(10);
✅好:
abstract class Shape { abstract getArea(): number; } class Rectangle extends Shape { constructor(protected width: number, protected height: number) { super(); } getArea(): number { return this.width * this.height; } } class Square extends Shape { constructor(protected side: number) { super(); } getArea(): number { return this.side * this.side; } } // Usage const shape: Shape = new Rectangle(10, 5); console.log(shape.getArea());
**为什么它更好?**
那么我应该如何在我的代码中正确有效地应用 LSP 呢?
=> **当子类与父类的行为不完全一致时,避免强制继承。**
[...继续下一课:)]