软件教程 2025年08月6日
0 收藏 0 点赞 555 浏览 3933 个字
摘要 :

文章目录 单一职责原则(SRP):让代码各司其职 1)反面案例: 2)正面案例: 开闭原则(OCP):让代码可扩展且稳定 1)反面案例: 2)正面案例: 里氏替换原则(LS……




  • 单一职责原则(SRP):让代码各司其职
    • 1)反面案例:
    • 2)正面案例:
  • 开闭原则(OCP):让代码可扩展且稳定
    • 1)反面案例:
    • 2)正面案例:
  • 里氏替换原则(LSP):保证子类的可替代性
    • 1)反面案例:
    • 2)正面案例:
  • 接口隔离原则(ISP):拒绝臃肿,只取所需
    • 1)反面案例:
    • 2)正面案例:
  • 依赖倒置原则(DIP):依赖抽象,远离具体
    • 1)反面案例:
    • 2)正面案例:
  • 什么SOLID原则在JavaScript中如此重要?

在JavaScript编程中SOLID原则就像是一套神奇的指南,能帮我们写出高质量、易维护的代码。它包含五个重要原则,分别从不同角度规范代码的设计和编写方式。接下来,我就结合实际的JavaScript示例,给大家详细讲讲这五个原则,让大家轻松掌握它们的精髓。

单一职责原则(SRP):让代码各司其职

简单来说,单一职责原则就是一个类或者函数只干一件事。打个比方,就像一个人在公司里只负责一个明确的工作任务一样。

1)反面案例:

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  saveToDatabase() {
    // 数据库逻辑
  }

  sendEmail() {
    // 邮件逻辑
  }
}

这段代码里的User类可不太“专一”,它既负责管理用户数据(constructor部分),又承担了保存数据到数据库(saveToDatabase方法)和发送邮件(sendEmail方法)的工作。这样一来,代码的职责就很混乱,后期维护和修改都容易出问题。

2)正面案例:

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

class UserRepository {
  saveToDatabase(user) { /* 数据库逻辑 */ }
}

class EmailService {
  sendEmail(user) { /* 邮件逻辑 */ }
}

改进之后,User类只专注于管理用户数据,UserRepository类负责和数据库交互保存数据,EmailService类专门处理邮件发送。每个类都有自己明确的职责,代码变得清晰明了,维护起来也轻松多了。

开闭原则(OCP):让代码可扩展且稳定

开闭原则的核心思想是,一个软件实体(比如类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当我们需要添加新功能时,最好是通过扩展现有代码来实现,而不是直接去修改原来的代码。

1)反面案例:

class Logger {
  logToConsole(message) {
    console.log(message);
  }

  logToFile(message) {
    // 写入文件
  }
}

// 要添加一个新的记录器(例如 HTTP),必须修改 Logger 类。

在这个Logger类中,如果我们想要新增一种记录日志的方式,比如记录到HTTP服务器,就不得不直接修改Logger类的代码。这样做的风险很大,可能会影响到原来已经正常运行的功能。

2)正面案例:

// 使用策略模式扩展行为
class Logger {
  log(message, loggerStrategy) {
    loggerStrategy(message);
  }
}

// 定义策略(扩展)
const consoleStrategy = (msg) => console.log(msg);
const fileStrategy = (msg) => writeToFile(msg);
const httpStrategy = (msg) => fetch(\'/log\', { body: msg }); // 添加新的记录器而无需修改 Logger 类

// 使用:
const logger = new Logger();
logger.log(\"Error!\", httpStrategy); // 无需修改 Logger

这里通过使用策略模式,Logger类的log方法接收一个策略函数loggerStrategy。当我们想要新增记录日志的方式时,只需要定义一个新的策略函数就行,完全不用修改Logger类本身的代码。这样既实现了功能扩展,又保证了原有代码的稳定性。

里氏替换原则(LSP):保证子类的可替代性

里氏替换原则要求子类必须能够替换它们的父类,并且不会影响程序的正常运行。也就是说,在使用父类的地方,都可以安全地用子类来代替。

1)反面案例:

class Rectangle {
  setWidth(w) { this.width = w }
  setHeight(h) { this.height = h }
}

class Square extends Rectangle {
  setSize(size) { // 违反 LSP
    this.width = size;
    this.height = size;
  }
}

function resizeShape(shape) {
  shape.setWidth(10);
  shape.setHeight(5); // 对于 Square 来说会出问题
}

在这个例子中,Square类继承自Rectangle类,但Square类中定义的setSize方法破坏了里氏替换原则。当resizeShape函数对一个Square对象进行操作时,就会出现不符合预期的结果,因为Square的宽高是相等的,不能像普通Rectangle那样分别设置宽高。

2)正面案例:

class Shape {
  area() { /* 抽象 */ }
}

class Rectangle extends Shape { /* 实现 area */ }
class Square extends Shape { /* 实现 area */ }

改进后,RectangleSquare都继承自抽象的Shape类,并且都实现了area方法。这样在使用Shape类的地方,无论是Rectangle还是Square都可以正常替换,不会出现问题。

接口隔离原则(ISP):拒绝臃肿,只取所需

接口隔离原则强调客户端不应该依赖那些它们根本用不到的接口。这就好比我们去超市买东西,只拿自己需要的,没必要把超市所有的商品都搬回家。

1)反面案例:

class Worker {
  work() { /* ... */ }
  eat() { /* ... */ }
}

// Robot 被迫实现 eat()
class Robot extends Worker {
  eat() { throw Error(\"Robots don\'t eat!\"); }
}

在这个例子中,Worker类定义了workeat两个方法,而Robot类继承自Worker类,但机器人根本不需要eat这个功能,却不得不实现它,这就导致了代码的不合理和臃肿。

2)正面案例:

class Workable {
  work() { /* 接口 */ }
}

class Eatable {
  eat() { /* 接口 */ }
}

class Human implements Workable, Eatable { /* ... */ }
class Robot implements Workable { /* ... */ }

改进后,把workeat两个功能分别定义在不同的接口中。Human类实现了WorkableEatable接口,因为人既会工作也会吃饭;而Robot类只实现Workable接口,因为机器人只需要工作,这样代码就简洁合理多了。

依赖倒置原则(DIP):依赖抽象,远离具体

依赖倒置原则告诉我们,在代码中应该依赖抽象(比如接口、抽象类),而不是依赖具体的实现类。这样可以降低代码之间的耦合度,提高代码的可维护性和可测试性。

1)反面案例:

class MySQLDatabase {
  save(data) { /* MySQL 特定 */ }
}

class UserService {
  constructor() {
    this.db = new MySQLDatabase(); // 紧耦合
  }
}

在这段代码里,UserService类直接依赖了MySQLDatabase这个具体的数据库实现类。如果后续要更换数据库,比如从MySQL换成MongoDB,就需要大量修改UserService类的代码,这显然不是我们想要的。

2)正面案例:

class Database {
  save(data) { /* 抽象 */ }
}

class MySQLDatabase extends Database { /* ... */ }
class MongoDB extends Database { /* ... */ }

class UserService {
  constructor(database) {
    this.db = database; // 注入依赖
  }
}

改进后,先定义了一个抽象的Database类,MySQLDatabaseMongoDB都继承自这个抽象类。UserService类通过构造函数注入Database类型的依赖,这样就和具体的数据库实现解耦了。如果要更换数据库,只需要更换注入的具体实现类就行,UserService类本身的代码基本不需要修改。

为什么SOLID原则在JavaScript中如此重要?

  1. 维护更轻松:遵循SOLID原则编写的代码,各个部分职责明确,修改某个功能时,影响的范围更小,不容易牵一发而动全身。
  2. 测试更方便:每个模块或者类的逻辑都是相对独立的,这样在进行单元测试时,更容易隔离和测试,提高测试的准确性和效率。
  3. 架构更灵活:面对不断变化的需求,能够轻松扩展和修改代码,而不需要大规模重写,让项目的架构更具弹性。
  4. 代码可重用性高:各个组件功能单一、职责明确,在不同的项目中,只要需求相似,就可以直接复用这些组件,节省开发时间和精力。

掌握了SOLID原则,我们就能编写出更优质、更易维护的JavaScript代码。希望大家都能把这些原则运用到实际开发中,让自己的代码更上一层楼!要是在理解或者应用过程中有什么问题,欢迎一起讨论。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/6751.html

管理员

相关推荐
2025-08-06

文章目录 一、Promise基础回顾 二、Promise 与 axios 结合使用场景及方法 (一)直接返回 axios …

270
2025-08-06

文章目录 一、模块初始化时的内部机制 二、常见导出写法的差异分析 (一)写法一:module.exports…

108
2025-08-06

文章目录 一、ResizeObserver详解 (一)ResizeObserver是什么 (二)ResizeObserver的基本用法 …

684
2025-08-06

文章目录 一、前期准备工作 (一)下载相关文件 (二)安装必要工具 二、处理扣子空间生成的文件…

340
2025-08-06

文章目录 一、官方文档 二、自动解包的数据类型 ref对象:无需.value即可访问 reactive对象:保持…

371
2025-08-06

文章目录 一、Hooks的工作原理 二、在if语句中使用Hook会出什么岔子? 三、React官方的Hook使用规…

844
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号