函数的参数列表是函数可变性的表现。
参数尽量避免重复,越短越容易理解,而且调用者调用函数也越轻松。
特征
函数参数列表过长,一些函数的参数总是成对出现。
重构方法
以查询取代参数
java
// 重构前
availableVacation(anEmployee, anEmployee.grade);
function availableVacation(anEmployee, grade) {
// calculate vacation...
// 重构后
availableVacation(anEmployee)
function availableVacation(anEmployee) {
const grade = anEmployee.grade;
// calculate vacation...
函数的参数列表应该总结该函数的可变性,标示出函数可能体现出行为差异的主要方式。和任何代码中的语句一样,参数列表应该尽量避免重复,并且参数列表越短就越容易理解。
如果调用函数时传入了一个值,而这个值由函数自己来获得也是同样容易,这就是重复。这个本不必要的参数会增加调用者的难度,因为它不得不找出正确的参数值,其实原本调用者是不需要费这个力气的。
如果函数本身很容易获取到一个参数,那就不需要传递。如果一个参数和函数本身没有任何依赖,函数不负责某一部分的责任和功能,那就没办法通过这种方式移除参数。
保持对象完整
java
// 重构前
onst low = aRoom.daysTempRange.low;
const high = aRoom.daysTempRange.high;
if (aPlan.withinRange(low, high)
// 重构后
if (aPlan.withinRange(aRoom.daysTempRange))
引入参数对象
java
// 重构前
function amountInvoiced(startDate, endDate) {...}
function amountReceived(startDate, endDate) {...}
function amountOverdue(startDate, endDate) {...}
// 重构后
function amountInvoiced(aDateRange) {...}
function amountReceived(aDateRange) {...}
function amountOverdue(aDateRange) {...}
一些数据项总是结对出现,可以使用一个数据结构代替它。
- 让数据项之间的关系变得明晰;
- 让函数参数列表缩短;
- 使用该数据的函数,会使用同样的名字访问其中元素,提升代码的一致性;
- 一旦提取出来,我们可以把散落在各个地方对这个数据的同样操作提取为函数。
移除标记参数
javascript
// 重构前
function setDimension(name, value) {
if (name === "height") {
this._height = value;
return;
}
if (name === "width") {
this._width = value;
return;
}
}
// 重构后
function setHeight(value) {this._height = value;}
function setWidth (value) {this._width = value;}
移除标记参数有布尔、枚举类型。
当调用一个函数时,传入的时字面量 true, false 的形式,那就需要重构。如果是变量的形式传入,那就不需要,比如下述代码。
java
boolean isRush = determineIfRush(anOrder);
deliveryDate(anOrder, isRush);
函数组合成类
javascript
// 重构前
function base(aReading) {...}
function taxableCharge(aReading) {...}
function calculateBaseCharge(aReading) {...}
// 重构后
class Reading {
base() {...}
taxableCharge() {...}
calculateBaseCharge() {...}
}
如果几个函数总是形影不离的操作同一个类型数据,那就可以把这块数据和这几个函数放到一个类里。
另外函数的参数列表是函数可变性的表现,所以可以根据参数的变化频率来判断是否应该作为函数的参数。
在这个例子中,bookId 和 httpClient 的变化频率是不一致的,bookId 是每次调用都会变化,而 httpClient 是每次调用都不会变化,所以 bookId 应该作为参数,而 httpClient 应该以其他形式调用。
java
public void getChapters(final long bookId,
final HttpClient httpClient,
final ChapterProcessor processor) {
HttpUriRequest request = createChapterRequest(bookId);
HttpResponse response = httpClient.execute(request);
List<Chapter> chapters = toChapters(response);
processor.process(chapters);
}
示例代码
java
// 重构前
public void createBook(final String title,
final String introduction,
final URL coverUrl,
final BookType type,
final BookChannel channel,
final String protagonists,
final String tags,
final boolean completed) {
...
Book book = Book.builder
.title(title)
.introduction(introduction)
.coverUrl(coverUrl)
.type(type)
.channel(channel)
.protagonists(protagonists)
.tags(tags)
.completed(completed)
.build();
this.repository.save(book);
}
// 重构后
public class NewBookParamters {
private String title;
private String introduction;
private URL coverUrl;
private BookType type;
private BookChannel channel;
private String protagonists;
private String tags;
private boolean completed;
public Book newBook() {
return Book.builder
.title(title)
.introduction(introduction)
.coverUrl(coverUrl)
.type(type)
.channel(channel)
.protagonists(protagonists)
.tags(tags)
.completed(completed)
.build();
}
}
public void createBook(final NewBookParamters parameters) {
...
Book book = parameters.newBook();
this.repository.save(book);
}