0%

《重构-改善既有代码设计》读书笔记

1. 为何重构

  • 重构改善软件设计,让代码易于理解
  • 重构帮助找到bug
  • 重构提高编程速度

2. 何时重构

  • 三次法则,事不过三,三则重构
  • 添加新功能时重构
  • 修补错误时重构
  • review代码时重构

3. 坏代码

  • Duplicated Code(重复代码)
  • Long Method(函数过长)
  • Large Class(大类)
  • Long Parameter List(参数过多)
  • So on

4. 准备工作

4.1 构建测试环境(重要)

5. 重构方法

5.1 重新组织函数

5.1.1 例子1:(提炼函数)
1
2
3
4
5
6
7
void printOwing(double amount) {
printBanner();

// print detail
System.out.println("name:" + _name);
System.out.println("amount:" + _amount);
}

改成

1
2
3
4
5
6
7
8
void printOwing(double amount) {
printBanner();
printDetails(amount);
}
void printDetails(double amount) {
System.out.println("name:" + _name);
System.out.println("amount:" + _amount);
}
5.1.2 例子2:(以查询代替临时变量)
1
2
3
4
5
6
7
double getPrice() {
int basePrice = _quantity * _itemPrice;
double discountFactor;
if (basePrice > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice * discountFactor;
}

改成

1
2
3
4
5
6
7
8
9
10
11
private int basePrice() {
return _quantity * _itemPrice;
}

private double discountFactor() {
if (basePrice() > 1000) return 0.95;
else return 0.98;
}
double getPrice() {
return basePrice() * discountFactor();
}
5.1.3 例子3:(引入解释性变量)
1
2
3
4
5
6
if ( (platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1) &&
{
wasInitialized() && resize > 0 )
// do something
}

改为:

1
2
3
4
5
6
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1; 
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
// do something
}
1
2
3
4
5
double price() {
// price is base price - quantity discount + shipping return _quantity * _itemPrice -
Math.max(0, _quantity - 500) * _itemPrice * 0.05 +
Math.min(_quantity * _itemPrice * 0.1, 100.0);
}

改为

1
2
3
4
5
6
7
8
9
10
11
12
double price() {
return basePrice() - quantityDiscount() + shipping();
}
private double quantityDiscount() {
return Math.max(0, _quantity - 500) * _itemPrice * 0.05;
}
private double shipping() {
return Math.min(basePrice() * 0.1, 100.0);
}
private double basePrice() {
return _quantity * _itemPrice;
}
5.1.4 例子4:(以函数对象取代函数)
1
2
3
4
5
6
7
class Order...
double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation;
}

改成:

1
2
Order
return new PriceCalculator(this).compute()

原理就是利用代理类来进行计算

5.2 在对象之间搬移特性

5.2 1 搬移函数

将一个类的函数搬移到另外一个类中,需要增加委托类

5.2.2 搬移字段

将一个类的字段搬移到另外一个类中,我想把表示利率的_interestRate搬移到AccountType class去

1
2
3
4
5
6
class Account...
private AccountType _type;
private double _interestRate;
double interestForAmount_days (double amount, int days) {
return _interestRate * amount * days / 365;
}

改成

1
2
3
4
5
6
7
8
9
10
11
12
13
class AccountType...
private double _interestRate;
void setInterestRate (double arg) {
_interestRate = arg;
}
double getInterestRate () {
return _interestRate;
}

private double _interestRate;
double interestForAmount_days (double amount, int days) {
return _type.getInterestRate() * amount * days / 365;
}
5.2.3 提炼类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person...
public String getName() {
return _name;
}
public String getTelephoneNumber() {
return ("(" + _officeAreaCode + ") " + _officeNumber);
}
String getOfficeAreaCode() {
return _officeAreaCode;
}
void setOfficeAreaCode(String arg) {
_officeAreaCode = arg;
}
String getOfficeNumber() {
return _officeNumber;
}
void setOfficeNumber(String arg) {
_officeNumber = arg;
}
private String _name;
private String _officeAreaCode;
private String _officeNumber;

因为后续TelephoneNumber可能会有扩展,因此将此提炼成一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class TelephoneNumber {
String getAreaCode() {
return _areaCode;
}
void setAreaCode(String arg) {
_areaCode = arg;
}
private String _areaCode;
}
class Person...
public String getTelephoneNumber() {
return ("(" + getOfficeAreaCode() + ") " + _officeNumber);
}
String getOfficeAreaCode() {
return _officeTelephone.getAreaCode();
}
void setOfficeAreaCode(String arg) {
_officeTelephone.setAreaCode(arg);
}

class Person...
public String getName() {
return _name;
}
public String getTelephoneNumber(){
return _officeTelephone.getTelephoneNumber();

TelephoneNumber getOfficeTelephone() {
return _officeTelephone;
}
private String _name;
private TelephoneNumber _officeTelephone = new TelephoneNumber();

class TelephoneNumber...
public String getTelephoneNumber() {
return ("(" + _areaCode + ") " + _number);
}
String getAreaCode() {
return _areaCode;
}
void setAreaCode(String arg) {
_areaCode = arg;
}
String getNumber() {
return _number;
}
void setNumber(String arg) {
_number = arg;
}
private String _number;
private String _areaCode;
5.2.3 将类内联化

是提炼类的反过程

5.2.4 隐藏Delegate

某些时候,一个功能要经过好多层委托,最后才能看到最终的实现,如果委托链过长,显然是会影响代码阅读的。一条有用的经验是委托层级不能超过三层。

5.3 重新组织数据

5.3.1 以对象取代数据值
1
2
3
4
5
6
7
8
9
10
11
class Order...
public Order (String customer) {
_customer = customer;
}
public String getCustomer() {
return _customer;
}
public void setCustomer(String arg) {
_customer = arg;
}
private String _customer;

改为,修改之后的代码扩展更加方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Customer {
public Customer (String name) {
_name = name;
}
public String getName() {
return _name;
}
private final String _name;
}

class Order...
public Order (String customer) {
_customer = new Customer(customer);
}
public String getCustomer() {
return _customer.getName();
}
private Customer _customer;
public void setCustomer(String arg) {
_customer = new Customer(customer);
}
5.3.2 以对象取代数组
5.3.3 以符号常量/字面常量取代魔法数

5.4 简化条件表达式(重要)

5.4.1 分解条件式

1
2
3
if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
else charge = quantity * _summerRate;

改为

1
2
3
4
5
6
7
8
9
10
11
12
if (notSummer(date))
charge = winterCharge(quantity);
else charge = summerCharge (quantity);
private boolean notSummer(Date date) {
return date.before (SUMMER_START) || date.after(SUMMER_END);
}
private double summerCharge(int quantity) {
return quantity * _summerRate;
}
private double winterCharge(int quantity) {
return quantity * _winterRate + _winterServiceCharge;
}
5.4.2 合并条件式
1
2
3
4
5
6
double disabilityAmount() {
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
...

改成

1
2
3
4
double disabilityAmount() {
if ((_seniority < 2) || (_monthsDisabled > 12) || (_isPartTime)) return 0;
// compute the disability amount
...
5.4.3 以卫语句取代嵌套条件式
1
2
3
4
5
6
7
8
9
10
11
12
double getPayAmount() {
double result;
if (_isDead) result = deadAmount();
else {
if (_isSeparated) result = separatedAmount();
else {
if (_isRetired) result = retiredAmount();
else result = normalPayAmount();
};
}
return result;
};

改为

1
2
3
4
5
6
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};

5.5 简化函数调用

5.6 处理概括关系

5.7 大型重构