[第一部分] 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 呢?
=> **当子类与父类的行为不完全一致时,避免强制继承。**
[...继续下一课:)]