6 简化函数调用

1 Introduce Parameter Object(引入参数对象)

在代码中,可能有一组参数总是一起被传递到好几个函数中,这样的一组参数就是所谓的Data Clumps(数据泥团)。最常见的就是指代一个时间范围的startTime/endTime。可以通过Introduce Parameter Object手法,以一个对象取代这些参数。

重构示例19

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 重构前
class Account {
public:
double GetFlowBetween(Date& startTime, Date& endTime)
{
double result = 0.0;
for (auto& entry : m_entries) {
if (entry.GetDate() == startTime || entry.GetDate() == endTime ||
(entry.GetDate().After(startTime) && entry.GetDate().Before(endTime)) {
result += entry.GetValue();
}
}
return result;
}
}
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
// 重构后
class Account {
public:
double GetFlowBetween(DateRange& dateRange)
{
double result = 0.0;
for (auto& entry : m_entries) {
if (dateRange.Includes(entry.GetDate())) {
result += entry.GetValue();
}
}
return result;
}
}

class DateRange {
public:
bool Includes(Date& date)
{
return date == m_startTime || date == m_endTime ||
(date.After(m_startTime) && date.Before(m_endTime));
}
private:
Date m_startTime;
Date m_endTime;
}

2 Replace Constructor with Factory Method(以工厂函数取代构造函数)

如果希望在创建对象时不仅仅是做简单的构建动作,可以将构造函数替换为静态工厂函数,并将原来的构造函数设为私有。静态工厂函数不仅在语义上更加符合人的思维,使代码可读性更强,它还能降低对象与对象使用者之间的耦合。比如,后续想要把对象改成单例模式,只需修改一下静态工厂方法,对象的使用者无感知。

Java 8新增的接口都采用了静态工厂函数用于创建对象,比如Optional接口、新的时间接口等。

3 Replace Parameter with Explicit Method(以明确函数取代参数)

如果某个参数有多种可能的值,而函数内又以条件表达式检查这些参数值,并根据不同参数值作出不同的行为,那么就可以使用Replace Parameter with Explicit Method进行重构了。该手法是提供了不同的函数给调用者使用,避免出现条件表达式。

重构示例20

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 重构前
static Employee* Create(int type)
{
switch (type) {
case ENGINEER:
return new Engineer;
case SALESMAN:
return new Salesman;
case MANAGER:
return new Manager;
default:
return nullptr;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 重构后
static Employee* CreateEngineer()
{
return new Engineer;
}
static Employee* CreateSalesman()
{
return new Salesman;
}
static Employee* CreateManager()
{
return new Manager;
}

4 Remove Setting Method(移除设置函数)

某些类中的某些字段应该在初始化时就确认值,后续都不会在变化。如果这样的字段拥有setter函数,就应该把它去掉,然后把字段设置为const/final。

过多无用的getter/setter函数是造成贫血模型的罪恶源头

重构示例21

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 重构前
class Account {
public:
Account(string id)
{
SetId(id);
}
void SetId(string id)
{
m_id = id;
}
private:
string m_id;
}
1
2
3
4
5
6
7
// 重构后
class Account {
public:
Account(string id) m_id(id) {}
private:
const string m_id;
}