💡 根据 遗忘曲线:如果没有记录和回顾,6天后便会忘记75%的内容
读书笔记正是帮助你记录和回顾的工具,不必拘泥于形式,其核心是:记录、翻看、思考
| 书名 | 重构:改善既有代码的设计(第2版) |
|---|---|
| 作者 | 马丁·福勒(Martin_Fo) |
| 状态 | 阅读中 |
| 简介 | 本书包含了代码中容易出现的问题冗余,💩山,代码耦合度高等问题,提出了对应的解决方案。让代码重构从小的点做起,最后实现整个模块的重构。 |
思维导图
用思维导图,结构化记录本书的核心观点。
重构技巧
1. 提炼函数
通过函数单一原则,将复杂函数按功能提炼出简单函数
使用场景:如果需要花时间浏览一段代码才能弄清楚它到底要干什么,那么这时候就应该将其提炼到一个函数中,并根据它所做的事命名。以后再读这段代码时,一眼就能知道这个函数的用途。
2. 函数参数化
通过参数形式,消除重复函数
如路由跳转等相似方法,可以通过传参封装函数
3. 策略模式
使用策略模式替换if-else或者switch-case
// 重构前代码
function getPrice(tag, originPrice) {
// 新人价格
if(tag === 'newUser') {
return originPrice > 50.1 ? originPrice - 50 : originPrice
}
// 返场价格
if(tag === 'back') {
return originPrice > 200 ? originPrice - 50 : originPrice
}
// 活动价格
if(tag === 'activity') {
return originPrice > 300 ? originPrice - 100 : originPrice
}
}
// 重构后
const priceHandler = {
newUser(originPrice){
return originPrice > 50.1 ? originPrice - 50 : originPrice
},
back(originPrice){
return originPrice > 200 ? originPrice - 50 : originPrice
},
activity(originPrice){
return originPrice > 300 ? originPrice - 100 : originPrice
}
}
function getPrice(tag, originPrice){
return priceHandler[tag](originPrice)
}
4. 提炼变量
若有的表达式比较复杂,难以阅读,则需要提炼一个简单局部变量将表达式分解存储
// ==================重构前==================
function price(order) {
//价格 = 商品原价 - 数量满减价 + 运费
return order.quantity * order.price -
Math.max(0, order.quantity - 500) * order.price * 0.05 +
Math.min(order.quantity * order.price * 0.1, 100);
}
// ==================重构后==================
function price(order) {
const basePrice = order.quantity * order.price;
const quantityDiscount = Math.max(0, order.quantity - 500) * order.price * 0.05;
const shipping = Math.min(basePrice * 0.1, 100);
return basePrice - quantityDiscount + shipping;
}
5. 拆分阶段
若一大段代码在同时处理两件不同的事,可以将其拆分成多个独立的模块
// ==================重构前==================
function priceOrder(product, quantity, shippingMethod) {
const basePrice = product.basePrice * quantity;
const discount = Math.max(quantity - product.discountThreshold, 0)
* product.basePrice * product.discountRate;
const shippingPerCase = (basePrice > shippingMethod.discountThreshold)
? shippingMethod.discountedFee : shippingMethod.feePerCase;
const shippingCost = quantity * shippingPerCase;
const price = basePrice - discount + shippingCost;
return price;
}
/*
该例中前两行代码根据商品信息计算订单中与商品相关的价格, 随后的两行则根据配送信息计算配送成本。
将这两块逻辑相对独立后,后续如果修改价格和配送的计算逻辑则只需修改对应模块即可。
*/
// ==================重构后==================
function priceOrder(product, quantity, shippingMethod) {
const priceData = calculatePricingData(product, quantity);
return applyShipping(priceData, shippingMethod);
}
// 计算商品价格
function calculatePricingData(product, quantity) {
const basePrice = product.basePrice * quantity;
const discount = Math.max(quantity - product.discountThreshold, 0)
* product.basePrice * product.discountRate;
return {basePrice, quantity, discount};
}
// 计算配送价格
function applyShipping(priceData, shippingMethod) {
const shippingPerCase = (priceData.basePrice > shippingMethod.discountThreshold)
? shippingMethod.discountedFee : shippingMethod.feePerCase;
const shippingCost = priceData.quantity * shippingPerCase;
return priceData.basePrice - priceData.discount + shippingCost;
}
6. 拆分循环
如果一个循环中做多件事,可以让一个循环只做一件事,性能方面只是由O(N)变为O(2N),一般数据量不是很大的情况下,不会有大的影响。主要是让代码可读性增强
// ==================重构前==================
const people = [
{ age: 20, salary: 10000 },
{ age: 21, salary: 15000 },
{ age: 22, salary: 18000 }
]
let youngest = people[0] ? people[0].age : Infinity;
let totalSalary = 0;
for (const p of people) {
// 查找最年轻的人员
if (p.age < youngest) youngest = p.age;
// 计算总薪水
totalSalary += p.salary;
}
console.log(`youngestAge: ${youngest}, totalSalary: ${totalSalary}`);
// ==================重构后==================
const people = [
{ age: 20, salary: 10000 },
{ age: 21, salary: 15000 },
{ age: 22, salary: 18000 }
]
let totalSalary = 0;
for (const p of people) {
// 只计算总薪资
totalSalary += p.salary;
}
let youngest = people[0] ? people[0].age : Infinity;
for (const p of people) {
// 只查找最年轻的人员
if (p.age < youngest) youngest = p.age;
}
console.log(`youngestAge: ${youngest}, totalSalary: ${totalSalary}`);
// ==================提炼函数==================
const people = [
{ age: 20, salary: 10000 },
{ age: 21, salary: 15000 },
{ age: 22, salary: 18000 }
]
console.log(`youngestAge: ${youngestAge()}, totalSalary: ${totalSalary()}`);
function totalSalary() {
let totalSalary = 0;
for (const p of people) {
totalSalary += p.salary;
}
return totalSalary;
}
function youngestAge() {
let youngest = people[0] ? people[0].age : Infinity;
for (const p of people) {
if (p.age < youngest) youngest = p.age;
}
return youngest;
}
// ==================使用工具类进一步优化==================
const people = [
{ age: 20, salary: 10000 },
{ age: 21, salary: 15000 },
{ age: 22, salary: 18000 }
]
console.log(`youngestAge: ${youngestAge()}, totalSalary: ${totalSalary()}`);
function totalSalary() {
return people.reduce((total,p) => total + p.salary, 0);
}
function youngestAge() {
return Math.min(...people.map(p => p.age));
}
7. 卫语句取代嵌套条件表达式
如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为“卫语句”
// ==================重构前==================
function payAmount(employee) {
let result;
if(employee.isSeparated) {
result = {amount: 0, reasonCode:"SEP"};
}
else {
if (employee.isRetired) {
result = {amount: 0, reasonCode: "RET"};
}
else {
result = someFinalComputation();
}
}
return result;
}
// ==================重构后==================
function payAmount(employee) {
if (employee.isSeparated) return {amount: 0, reasonCode: "SEP"};
if (employee.isRetired) return {amount: 0, reasonCode: "RET"};
return someFinalComputation();
}