一、基本信息
中文名:《重构 改善既有代码的设计(第2版)》 |
英文名:《Refactoring improving the design of existing code》 |
作者:Martin Fowler 作者网站链接:https://martinfowler.com/
|
译者:熊节 林从⽻
|
二、电子书链接
-
https://gausszhou.github.io/refactoring2-zh/
-
https://book-refactoring2.ifmicro.com/
三、书籍结构
本书的重点包含两部分:(1)识别代码中的“坏味道”;(2)重构手法
- 第一章通过剧院售票结算程序来展示代码如何进行重构
- 第二章介绍重构的原则
- 第三章比较出名,介绍了24中代码的坏味道
- 第四章介绍构建测试体系
- 第五章介绍重构名录
- 第六到十二章介绍了61种重构手法
四、第一章简要笔记
1. 示例程序
剧院售票结算程序;基本逻辑如下:
剧名 | 类型 |
《哈姆雷特》
|
悲剧
|
《皆⼤欢喜》
|
喜剧
|
《奥塞罗》
|
悲剧
|
票价计算⽅式:观众数量x |
积分计算⽅式:
| |
悲剧
|
x <= 30: 40000
x > 30: 40000 + 1000 * (x - 30)
|
max(x - 30, 0)
|
喜剧
|
x <= 20: 30000 + 300 * x
x > 20: 30000 + 300 * x + 10000 + 50 * (x - 20)
|
max(x - 30, 0) + floor(x / 5)
|
计算并打印输出票价和积分
function statement(invoice, plays) {
let totalAmount = 0
let volumeCredits = 0
let result = `Statement for ${invoice.customer}\n`
const format = new Intl.NumberFormat("en-us",
{
style: "currency",
currency: "USD",
minimumFractionDigits: 2
}).format
for (let perf of invoice.performances) {
const play = plays[perf.playID]
let thisAmount = 0
switch (play.type) {
case "tragedy":
thisAmount = 40000;
if (perf.audience > 30) {
thisAmount += 1000 * (perf.audience - 30)
}
break
case "comedy":
thisAmount = 30000;
if (perf.audience > 20) {
thisAmount += 10000 + 500 * (perf.audience - 20)
}
thisAmount += 300 * perf.audience
break
default:
throw new Error(`unknown type: ${play.type}`)
}
volumeCredits += Math.max(perf.audience - 30, 0)
if ("comedy" === play.type)
volumeCredits += Math.floor(perf.audience / 5)
result += ` ${play.name}, ${format(thisAmount / 100)} (${perf.audience} seats) \n`
totalAmount += thisAmount
}
result += `Amount owed is ${format(totalAmount / 100)} \n`
result += `You earned ${volumeCredits} credits\n`
return result
}
输入参数:
let plays = {
"hamlet": {
"name": "Hamlet",
"type": "tragedy"
},
"as-like": {
"name": "As You Like It",
"type": "comedy"
},
"othello": {
"name": "Othello",
"type": "tragedy"
}
}
let invoices = [
{
"customer": "BigCo",
"performances": [
{
"playID": "hamlet",
"audience": 55
},
{
"playID": "as-like",
"audience": 35
},
{
"playID": "othello",
"audience": 40
}
]
}
]
let result = statement(invoices[0], plays)
输出结果:
Statement for BigCo
Hamlet: $650.00 (55 seats)
As You Like It: $580.00 (35 seats)
Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
从数据结果看:已满足程序要求
若增加两项需求:
- 以HTML格式输出结果
- 再增加多种戏剧类型
当前的程序便无法满足新的需求,因此需要对代码进行重构。
需求的变化使重构变得必要。如果⼀段代码能正常⼯作,并且不会再被修改,那么完全可以不去重构它
如果你要给程序添加⼀个特性,但发现代码因缺乏良好的结构⽽不易于进⾏更改,那就先重构那个程序,使其⽐较容易添加该特性,然后再添加该特性。
2. 重构步骤
第一步:
保证修改的代码拥有⼀组可靠的测试,这些测试必须有⾃我检验能⼒;
第二步:发现代码中⼀段⼩的“坏味道”,采用对应重构⼿法进⾏重构;
举例:
中间switch, 可以采用提炼函数将其提炼出来
function statement (invoice, plays) {
let totalAmount = 0;
let volumeCredits = 0;
let result = `Statement for ${invoice.customer}\n`;
const format = new Intl.NumberFormat("en-US",
{ style: "currency", currency: "USD",
minimumFractionDigits: 2 }).format;
for (let perf of invoice.performances) {
const play = plays[perf.playID];
let thisAmount = 0;
switch (play.type) {
case "tragedy":
thisAmount = 40000;
if (perf.audience > 30) {
thisAmount += 1000 * (perf.audience - 30);
}
break;
case "comedy":
thisAmount = 30000;
if (perf.audience > 20) {
thisAmount += 10000 + 500 * (perf.audience - 20);
}
thisAmount += 300 * perf.audience;
break;
default:
throw new Error(`unknown type: ${play.type}`);
}
// add volume credits
volumeCredits += Math.max(perf.audience - 30, 0);
// add extra credit for every ten comedy attendees
if ("comedy" === play.type) volumeCredits += Math.floor(perf.audience / 5);
// print line for this order
result += ` ${play.name}: ${format(thisAmount/100)} (${perf.audience} seats)\n`;
totalAmount += thisAmount;
}
result += `Amount owed is ${format(totalAmount/100)}\n`;
result += `You earned ${volumeCredits} credits\n`;
return result;
}
第三步:编译并执⾏测试
重构技术就是以微⼩的步伐修改程序。如果你犯下错误,很容易便可发现它
第四步:
测试通过commit提交
重构的节奏感,每⼀步都保证代码处于编译通过和测试通过的可⼯作状
第五步:循环第二三四步直到重构结果令人满意
五、代码的“坏味道”
待整理.....