重构小技巧

提炼函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function printOwing(invoice) {
printBanner();
let outstanding = calculateOutstanding();
//print details
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}

// 重构后
function printOwing(invoice) {
printBanner();
let outstanding = calculateOutstanding();
printDetails(outstanding);
function printDetails(outstanding) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
}

重构前函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function printOwing(invoice) {
let outstanding = 0;
printBanner();
// calculate outstanding
for (const o of invoice.orders) {
outstanding += o.amount;
}
// record due date
const today = Clock.today;
invoice.dueDate = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 30
); //print details
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
}

范例:有局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function printOwing(invoice) {
let outstanding = 0;
printBanner();
// calculate outstanding
for (const o of invoice.orders) {
outstanding += o.amount;
}
// record due date
recordDueDate(invoice);
printDetails(invoice, outstanding);
}
function recordDueDate(invoice) {
const today = Clock.today;
invoice.dueDate = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 30
);
}
function printDetails(invoice, outstanding) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
}

范例:对局部变量再赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
function printOwing(invoice) {
printBanner();
const outstanding = calculateOutstanding(invoice);
recordDueDate(invoice);
printDetails(invoice, outstanding);
}
function calculateOutstanding(invoice) {
let result = 0;
for (const o of invoice.orders) {
result += o.amount;
}
return result;
}

内联函数

1
2
3
4
5
6
7
8
9
10
function getRating(driver) {
return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}
function moreThanFiveLateDeliveries(driver) {
return driver.numberOfLateDeliveries > 5;
}
// 重构后
function getRating(driver) {
return driver.numberOfLateDeliveries > 5 ? 2 : 1;
}

提炼变量

1
2
3
4
5
6
7
8
9
10
11
return (
order.quantity * order.itemPrice -
Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +
Math.min(order.quantity * order.itemPrice * 0.1, 100)
);
// 重构后
const basePrice = order.quantity * order.itemPrice;
const quantityDiscount =
Math.max(0, order.quantity - 500) * order.itemPrice * 0.05;
const shipping = Math.min(basePrice * 0.1, 100);
return basePrice - quantityDiscount + shipping;

重构前函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Order {
constructor(aRecord) {
this._data = aRecord;
}
get quantity() {
return this._data.quantity;
}
get itemPrice() {
return this._data.itemPrice;
}
get price() {
return (
this.quantity * this.itemPrice -
Math.max(0, this.quantity - 500) * this.itemPrice * 0.05 +
Math.min(this.quantity * this.itemPrice * 0.1, 100)
);
}
}

范例:在一个类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Order {
constructor(aRecord) {
this._data = aRecord;
}
get quantity() {
return this._data.quantity;
}
get itemPrice() {
return this._data.itemPrice;
}
get price() {
return this.basePrice - this.quantityDiscount + this.shipping;
}
get basePrice() {
return this.quantity * this.itemPrice;
}
get quantityDiscount() {
return Math.max(0, this.quantity - 500) * this.itemPrice * 0.05;
}
get shipping() {
return Math.min(this.basePrice * 0.1, 100);
}
}

内联变量

1
2
3
4
let basePrice = anOrder.basePrice;
return basePrice > 1000;
// 重构后
return anOrder.basePrice > 1000;

改变函数声明

1
2
3
function circum(radius) {...}
// 重构后
function circumference(radius) {...}

重构前函数

1
2
3
4
function inNewEngland(aCustomer) {
return ['MA', 'CT', 'ME', 'VT', 'NH', 'RI'].includes(aCustomer.address.state);
}
const newEnglanders = someCustomers.filter(c => inNewEngland(c));

范例:把参数改为属性

1
2
3
4
function inNewEngland(stateCode) {
return ['MA', 'CT', 'ME', 'VT', 'NH', 'RI'].includes(stateCode);
}
const newEnglanders = someCustomers.filter(c => inNewEngland(c.address.state));

封装变量

1
2
3
4
5
6
7
8
9
let defaultOwner = { firstName: 'Martin', lastName: 'Fowler' };
// 重构后
let defaultOwnerData = { firstName: 'Martin', lastName: 'Fowler' };
export function defaultOwner() {
return defaultOwnerData;
}
export function setDefaultOwner(arg) {
defaultOwnerData = arg;
}

控制引用变量修改

1
2
3
4
5
6
7
8
9
let defaultOwner = { firstName: 'Martin', lastName: 'Fowler' };
// 重构后
let defaultOwnerData = { firstName: 'Martin', lastName: 'Fowler' };
export function defaultOwner() {
return Object.assign({}, defaultOwnerData);
}
export function setDefaultOwner(arg) {
defaultOwnerData = arg;
}

函数改名

1
2
3
let a = height * width;
// 重构后
let area = height * width;

引入参数对象

1
2
3
4
5
6
7
function amountInvoiced(startDate, endDate) {...}
function amountReceived(startDate, endDate) {...}
function amountOverdue(startDate, endDate) {...}
// 重构后
function amountInvoiced(aDateRange) {...}
function amountReceived(aDateRange) {...}
function amountOverdue(aDateRange) {...}

重构前函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const station = {
name: 'ZB1',
readings: [
{ temp: 47, time: '2016-11-10 09:10' },
{ temp: 53, time: '2016-11-10 09:20' },
{ temp: 58, time: '2016-11-10 09:30' },
{ temp: 53, time: '2016-11-10 09:40' },
{ temp: 51, time: '2016-11-10 09:50' }
]
};
// 指定范围的温度
function readingsOutsideRange(station, min, max) {
return station.readings.filter(r => r.temp < min || r.temp > max);
}
// 调用方
alerts = readingsOutsideRange(
station,
operatingPlan.temperatureFloor,
operatingPlan.temperatureCeiling
);

第一步:将最大值、最小值生命为一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class NumberRange {
constructor(min, max) {
this._data = {
min,
max
};
}
get min() {
return this._data.min;
}
get max() {
return this._data.max;
}
}

第二步:修改判断逻辑

1
2
3
4
5
6
7
const range = new NumberRange(
operatingPlan.temperatureFloor,
operatingPlan.temperatureCeiling
);
function readingsOutsideRange(station, range) {
return station.readings.filter(r => r.temp < range.min || r.temp > range.max);
}

第三步:将判断逻辑封装函数

1
2
3
4
5
6
7
8
9
function readingsOutsideRange(station, range) {
return station.readings.filter(r => !range.contains(r.temp));
}
class NumberRange {
// 省略其他代码
contains(arg) {
return arg >= this.min && arg <= this.max;
}
}

函数组合成类

1
2
3
4
5
6
7
8
9
function base(aReading) {...}
function taxableCharge(aReading) {...}
function calculateBaseCharge(aReading) {...}
// 重构后
class Reading {
base() {...}
taxableCharge() {...}
calculateBaseCharge() {...}
}

重构前函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
reading = { customer: 'ivan', quantity: 10, month: 5, year: 2017 };
// 客户端1
const aReading = acquireReading();
const baseCharge = baseRate(aReading.month, aReading.year) * aReading.quantity;
// 客服端2
const aReading = acquireReading();
const base = baseRate(aReading.month, aReading.year) * aReading.quantity;
const taxableCharge = Math.max(0, base - taxThreshold(aReading.year));
// 客户端3
const aReading = acquireReading();
const basicChargeAmount = calculateBaseCharge(aReading);
function calculateBaseCharge(aReading) {
return baseRate(aReading.month, aReading.year) * aReading.quantity;
}

第一步:封装类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Reading {
constructor(data) {
this._customer = data.customer;
this._month = data.month;
this._quantity = data.quantity;
this._year = data.year;
}
get month() {
return this._month;
}
get year() {
return this._year;
}
get customer() {
return this._customer;
}
get quantity() {
return this._quantity;
}
}

第二步:搬移已有函数

1
2
3
4
5
6
7
8
9
10
11
12
13
class Reading {
get calculateBaseCharge() {
return baseRate(this.month, this.year) * this.quantity;
}
}
// 函数改名
get baseCharge() {
return baseRate(this.month, this.year) * this.quantity;
}
// 客户端3
const rawReading = acquireReading();
const aReading = new Reading(rawReading);
const basicChargeAmount = aReading.baseCharge;

函数组合成变换

1
2
3
4
5
6
7
8
9
function base(aReading) {...}
function taxableCharge(aReading) {...}
// 重构后
function enrichReading(argReading) {
const aReading = _.cloneDeep(argReading);
aReading.baseCharge = base(aReading);
aReading.taxableCharge = taxableCharge(aReading);
return aReading;
}

重构前函数同函数组合成类一致

重构后

1
2
3
4
5
6
7
8
9
10
11
12
function enrichReading(original) {
const result = _.cloneDeep(original);
result.baseCharge = calculateBaseCharge(result);
result.taxableCharge = Math.max(
0,
result.baseCharge - taxThreshold(result.year)
);
return result;
}
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const baseCharge = aReading.baseCharge;

缺点

如果在代码地方修改了原始数据(reading),会出现数据不一致的情况。避免这种情况的方式是使用函数组合成类这种手法

拆分阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const orderData = orderString.split(/\s+/);
const productPrice = priceList[orderData[0].split('-')[1]];
const orderPrice = parseInt(orderData[1]) * productPrice;
// 重构后
const orderRecord = parseOrder(order);
const orderPrice = price(orderRecord, priceList);
function parseOrder(aString) {
const values = aString.split(/\s+/);
return {
productID: values[0].split('-')[1],
quantity: parseInt(values[1])
};
}
function price(order, priceList) {
return order.quantity * priceList[order.productID];
}

重构前函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}

第一步:提炼函数

1
2
3
4
5
6
7
8
9
10
11
12
function priceOrder(product, quantity, shippingMethod) {
const basePrice = product.basePrice * quantity;
const discount = Math.max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
const price = applyShipping(basePrice, shippingMethod, quantity, discount);
retuen price;
}
function applyShipping(basePrice, shippingMethod, quantity, discount) {
const shippingPerCase = (basePrice > shippingMethod.discountThreshold) ? shippingMethod.discountedFee : shippingMethod.feePerCase;
const shippingCost = quantity * shippingPerCase;
const price = basePrice - discount + shippingCost;
return price;
}

第二步:中转数据(引入参数对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
}

版权声明

以上代码来自重构 第2版:改善既有代码的设计,在此感谢