最近又再一次阅读了一下《重构 改善既有代码的设计》这本书,这实在是一本宝书!
《重构 改善既有代码的设计》这本书的精髓在于提供了一套系统的方法论和实践技巧,帮助开发者在不改变代码外在行为的前提下,提高代码的可读性、可维护性和可扩展性。以下是一些关键的重构原则和技巧,可以指导前端开发者进行更好的重构
重构的一些基本原则
1. 代码的坏味道:要进行重构,首先要识别代码中的问题,即“坏味道”。如重复代码、过长函数、过大类、过长参数列表等。当你发现这些问题时,就需要考虑进行重构。
2. 小步重构:重构的过程应该是一系列小步骤的累积,每次只做一点小的修改,然后进行测试。这样可以保证重构过程中不引入新的错误,也更容易回退。
3. 保持功能的一致性:重构的目的是改善代码的内部结构,而不是添加新功能。在重构过程中,要确保代码的外部行为保持不变。
4. 提炼函数:将复杂的函数拆分成多个小函数,每个函数只做一件事,这样可以提高代码的可读性和可维护性。
5. 合并重复的代码:如果发现有重复的代码,可以考虑将它们合并成一个函数或者类,以减少代码的重复度。
6. 优化数据结构:合理地组织数据结构,可以降低代码的复杂度,提高代码的可读性。例如,可以使用对象代替数组,或者使用更合适的数据结构如Map、Set等。
7. 引入解释性变量:使用有意义的变量名,可以让代码更容易理解。例如,将复杂的条件表达式提取成一个布尔变量,或者将复杂的计算结果赋值给一个具有描述性名称的变量。
8. 优化条件表达式:简化条件表达式,例如使用三元操作符代替if-else,或者将多个条件判断合并成一个函数。
9. 面向对象设计原则:在重构过程中,遵循面向对象设计原则,如单一职责原则(SRP)、开放封闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)。
10. 代码审查和测试:在重构过程中,要不断地进行代码审查和测试,确保重构没有引入新的错误,同时也可以发现潜在的问题,为进一步的重构提供方向。
前端同学在重构上的思考
虽然这本书作者是使用java来举例写的,但是其本质上描述的是一种思想,所以,我们作为前端开发者,在进行重构时,可以关注以下几点:
1. 优化组件结构:合理地拆分和组织前端组件,使得每个组件具有清晰的职责和易于理解的结构。
2. 提高代码复用:尽量减少重复代码,将通用功能抽象成组件、函数或者工具类,提高代码的复用性。
3. 状态管理:对于复杂的前端应用,可以使用状态管理工具(如Redux、Vuex等)来管理应用状态,使得状态变更更加可预测和可维护。
4. 性能优化:在重构过程中,关注前端性能,如减少不必要的渲染、优化网络请求等。
5. 代码规范和最佳实践:遵循前端开发的最佳实践和代码规范,如使用ESLint、Prettier等工具进行代码检查和格式化,确保代码风格的一致性。
6. 优化用户体验:在重构过程中,关注用户体验的优化,如提高页面响应速度、优化交互设计等。
7. 提高可维护性:对于经常变化的部分,可以考虑将其抽象成配置文件或者使用设计模式,如策略模式、观察者模式等,以提高代码的可维护性。
8. 单元测试和集成测试:编写单元测试和集成测试,确保重构过程中不引入新的错误,同时也可以作为重构的参考依据。
9. 文档和注释:编写清晰的文档和注释,帮助自己和团队成员更好地理解代码结构和功能。
10. 持续集成和持续部署:使用持续集成(CI)和持续部署(CD)工具,自动化测试和部署过程,确保重构后的代码能够快速、稳定地上线。
前端开发者在进行重构时,应关注代码质量、性能、用户体验等方面的优化,以提高代码的可读性、可维护性和可扩展性。同时,通过遵循一定的设计原则和最佳实践,以及使用工具和测试来辅助重构过程,确保重构的顺利进行。
另外,很多朋友说不敢重构,怕影响到现网功能,这本书提供了一个思路不妨利用起来,即先写测试,再重构,最后使测试通过才上线,可以关注上面的第8个例子,这也反向验证了,讲代码写的可测试的重要性,前端代码一定要考虑界面和逻辑解耦,逻辑部分是比较容易写单侧的,而ui部分,普遍认为不容易单侧,只适合做统一的E2E测试。
一个实际重构的例子
基于上面的这些原则,和实践准则,我们不妨来看一个具体的例子:
假设有一个计算订单折扣的函数:
代码语言:javascript复制function getDiscount(order) {
let discount = 0;
if (order.type === 'normal') {
// 正常订单的折扣逻辑
} else if (order.type === 'groupon') {
// 团购订单的折扣逻辑
} else if (order.type === 'promo') {
// 促销订单的折扣逻辑
}
return discount;
}
这个函数存在以下问题:
- 函数较长,包含所有订单类型的折扣逻辑
- 不同类型订单折扣逻辑混杂在一起,可读性差
- 如果要新增订单类型,需要修改此函数,容易引入bug
我们可以这样重构:
1. 提取订单类型判断到一个独立函数getOrderType:
代码语言:javascript复制function getOrderType(order) {
if (order.type === 'normal') {
return 'normal';
} else if (...省略) {
return 'promo';
}
}
2. 把每个类型订单的折扣逻辑提取为独立函数:
代码语言:javascript复制function getNormalDiscount(order) {
// 正常订单折扣逻辑
}
function getGrouponDiscount(order) {
// 团购订单折扣逻辑
}
function getPromoDiscount(order) {
// 促销订单折扣逻辑
}
3. 重构后的getDiscount函数简化为:
代码语言:javascript复制function getDiscount(order) {
const orderType = getOrderType(order);
switch(orderType) {
case 'normal':
return getNormalDiscount(order);
case 'groupon':
return getGrouponDiscount(order);
case 'promo':
return getPromoDiscount(order);
}
}
这样重构后,代码的职责划分更清晰,订单类型的判断和折扣逻辑分离开来,符合单一职责原则,而且还符合开放封闭原则。新订单类型的折扣规则可以简单通过新增函数来实现,无需修改已有代码。