# 重构 常见重构手法 (上)

qinfanpeng · 发布于 2016年12月21日 · 最后由 emanon 回复于 2017年01月20日 · 3925 次阅读

[slide]

## Agenda

• Introduce Explaining Variable
• Replace Temp with Query
• Inline Temp
• Extract Method
• Inline Method
• Substitue Algorithm
• Split Temoary Variable
• Remove Assignments to Parameters

[slide]

## Agenda

• Introduce Explaining Variable
• Replace Temp with Query
• Inline Temp
• Extract Method
• Inline Method
• Substitue Algorithm
• Split Temoary Variable
• Remove Assignments to Parameters

[slide]

``````const calculatePrice = ({ itemPrice, quantity }) => {
// price is base price - quantity discount + shipping
return itemPrice * quantity -
Math.max(0, quantity - 500) * itemPrice * 0.05 +
Math.min(100, itemPrice * quantity * 0.1)
}
``````

### 重构信号？

• 表达式逻辑杂糅于细节中，复杂难懂
• 可读性不高
• 重复

[slide]

## Introduce Explaining Variable(引入解释变量)

``````const calculatePrice = ({ itemPrice, quantity }) => {
// price is base price - quantity discount + shipping
return itemPrice * quantity -
Math.max(0, quantity - 500) * itemPrice * 0.05 +
Math.min(100, itemPrice * quantity * 0.1)
}
``````
``````const calculatePrice = ({ itemPrice, quantity }) => {
const basePrice = itemPrice * quantity
const quantityDiscount = Math.max(0, quantity - 500) * itemPrice * 0.05
const shipping = Math.min(100, basePrice * 0.1)

return basePrice - quantityDiscount + shipping
}

``````

[note] Demo 好处？

1. 把主逻辑从底层细节中剥离了，更可读
2. 代码自身就充当了注释的作用，
3. 消除了重复 [/note]

[slide]

``````const calculatePrice = ({ itemPrice, quantity }) => {
const basePrice = itemPrice * quantity
const quantityDiscount = Math.max(0, quantity - 500) * itemPrice * 0.05
const shipping = Math.min(100, basePrice * 0.1)

return basePrice - quantityDiscount + shipping
}
``````

• 是否考虑重用
• 是否会滋生更长的函数
• 变量是否值回票价

[slide]

## Replace Temp with Query(用查询取代临时变量)

``````const calculatePrice = ({ itemPrice, quantity }) => {
const basePrice = itemPrice * quantity
const quantityDiscount = Math.max(0, quantity - 500) * itemPrice * 0.05
const shipping = Math.min(100, basePrice * 0.1)

return basePrice - quantityDiscount + shipping
}
``````
``````const calculatePrice = (order) => {
return basePrice(order) - quantityDiscount(order) + shipping(order)
}

const basePrice = ({ itemPrice, quantity }) => {
return itemPrice * quantity
}

const quantityDiscount = ({ itemPrice, quantity }) => {
return Math.max(0, quantity - 500) * itemPrice * 0.05
}

var shipping = function (order) {
return Math.min(100, basePrice(order) * 0.1)
}
``````

[note] 好处？

• 代码即注释，一目了然
• 更可重用
• 抽象层级更协调 [/note]

[slide]

## Performance concern of More Small Functions

• 即使有细小的区别，代码可读性价值更高
• 若真有大问题，回退并不麻烦
• 总之，性能顾虑不能成为我们不抽方法的理由

[slide]

``````const isBigDeal = (order) => {
const basePrice = order.basePrice
return basePrice > 1000
}
``````

[slide]

## Inline Temp Variable

``````const isBigDeal = (order) => {
const basePrice = order.basePrice
return basePrice > 1000
}
``````
``````const isBigDeal = (order) => {
return order.basePrice > 1000
}
``````

[slide]

``````let temp = 2 * height * width
console.log('perimeter: ', temp)

temp = height * width
console.log('area: ', temp)
``````

### 重构信号？

• 对同一个变量多次赋值（并且内容并无关联） ，职责不单一

[slide]

## Split Temoary Variable(分解临时变量)

``````let temp = 2 * height * width
console.log('perimeter: ', temp)

temp = height * width
console.log('area: ', temp)
``````
``````const perimeter = 2 * height * width
console.log('perimeter: ', perimeter)

const area = height * width
console.log('area: ', area)
``````

[slide]

``````const users = [
{ user: 'barney', age: 36, active: true },
{ user: 'fred',   age: 40, active: false }
]

const activeUsers = []
users.each(user => {
if (user.active) {
activeUsers.push(user)
}
})
``````

### 遇到循环的时候，多斟酌一下

• 不利于`Parallelize`
• 尽量熟悉集合的API，如filtermapreducefind

[slide]

## Substitue Algorithm(替换算法)

``````const users = [
{ user: 'barney', age: 36, active: true },
{ user: 'fred',   age: 40, active: false }
]

const activeUsers = []
users.each(user => {
if (user.active) {
activeUsers.push(user)
}
})
``````
``````const activeUsers = filter(users, user => user.active)
``````
``````const activeUsers = filter(users, 'active')
``````

[slide]

## More Examples of Substitue Algorithm

``````const users = [
{ user: 'barney', age: 36, active: true },
{ user: 'fred',   age: 40, active: false }
]
``````

[magic data-transition="cover-circle"]

``````let activeUser = null

users.each(user => {
if (user.active) {
activeUser = user
break
}
})

``````

====

``````let totalAge = 0
users.each(user => {
if (user.active) {
totalAge += user.age
}
})
``````

[/magic]

[slide]

``````const discount = (inputVal, quantity) => {
if (inputVal > 50) inputVal -= 2
// ...
return inputVal
}
``````

### 重构信号？

• 对参数赋值，参数为引用时，极易引入副作用
• ```javascript const aMethod = (aObj) => { aObj.modifyInSomeWay() // That's ok aObj = anotherObj // Bad } ```

[slide]

## Remove Assignments to Parameters(移除对参数的赋值)

``````const discount = (inputVal, quantity) => {
if (inputVal > 50) inputVal -= 2
// ...
return inputVal
}
``````
``````const discount = (basePrice, quantity) => {
const finalPrice = basePrice
if (basePrice > 50) finalPrice -= 2
// ...
return finalPrice
}

``````

[slide]

## Extract Method(提取方法)

``````const printOwing = (orders) => {
let outstanding = 0.0
// print banner
console.log('*********************************')
console.log('********* Customer Owes *********')
console.log('*********************************')
for (let i = 0; i < orders.length; i++) {
outstanding += orders[i].amount
}
// print details
console.log('name: ', name)
console.log('amount: ', outstanding)
}
``````

### 重构信号？

• 注释
• 抽象层次不协调，以致于主逻辑淹没其中

[slide]

## Extract Method(提取方法)

``````const printOwing = (orders) => {
printBanner()

// print details
console.log('name: ', name)
console.log('amount: ', outstanding)
}
``````
``````const printOwing = (orders) => {
printBanner()
const outstanding = calculateOutstanding(orders)
printDetails(outstanding)
}

const printBanner = () => {
console.log('*********************************')
console.log('********* Customer Owes *********')
console.log('*********************************')
}

var calculateOutstanding = function (orders) {
return sumBy(orders, 'amount')
}

const printDetails = function (outstanding) {
console.log('name: ', name)
console.log('amount: ', outstanding)
}
``````

[slide]

## Inline Method(内联方法)

``````const getRating = () => {
return moreThanFiveNegativeFeedbacks() ? 1 : 2
}

const moreThanFiveNegativeFeedbacks = () => {
return this.negativeFeedbacks.length > 5
}

``````
``````const getRating = () => {
return this.negativeFeedbacks.length > 5 ? 1 : 2
}

``````

[slide]

[slide]

1. 《重构》
2. 《编写可读的艺术》
3. 《代码整洁之道》

[slide]

## Thank You & QA

#2楼 @emanon 让他去改自己半年前写的代码，他就知道厉害了。

#3楼 @qinfanpeng 这个我觉得不一定吧。改遗留代码好像更多时候仅仅只是让人觉得难改，但是对认识到自己平时开发中的各种坏习惯并没有帮助。而且多数程序员好像总会有各种办法在原来的基础上打补丁的。就更别说从中总结出好的写法了。情况好像往往是程序员一边难过地写代码，一边还认为自己的各种想法和做法始终是对的。你要说一个方法的参数超过 2 个很多时候是个 bad smell, 多数人只会笑话你，然后继续写出自以为正确的代码，然后继续痛苦。

#4楼 @emanon 确实有你说的这类略固执的人，对于接受不了意见的人，确认不好弄。

#6楼 @easonlovewan 你去维护一个10年以上的项目，你就知道有没有意义了

#4楼 @emanon 结对和code diff，给他们看什么是好的代码以及这样写有什么道理，一般的人都能接受意见了。对于影响不了的人，怎么都影响不了，可以提出团队了

lgn21st 将本帖设为了精华贴 12月21日 17:01

#13楼 @lgn21st 没那么多，就两个。简单期间，故意把OO相关的东西给剔除去了。

#8楼 @reverocean 看项目中别人写的代码都可以感觉到合理结构和注释的重要性。。。可惜，没时间重构别人的代码

#18楼 @wanglizheng 非常赞同，其实 sense 到位了，前期就会写出质量高很多的代码。

tesla_lee 中提及了此贴 01月12日 18:36

#4楼 @emanon 实际的项目不是复杂，而是做了两三个月，上线一个月， 老板就喊停了。 项目就黄了。 代码写那么好干嘛？

#21楼 @u1440247613 这样说，写代码还得先精通算命，写之前给这个项目算一卦。