代码坏味道及其重构 4 过长参数列表、数据泥团

函数的参数列表是函数可变性的表现。

参数尽量避免重复,越短越容易理解,而且调用者调用函数也越轻松。

特征

函数参数列表过长,一些函数的参数总是成对出现。

重构方法

以查询取代参数

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) {...}

一些数据项总是结对出现,可以使用一个数据结构代替它。

  1. 让数据项之间的关系变得明晰;
  2. 让函数参数列表缩短;
  3. 使用该数据的函数,会使用同样的名字访问其中元素,提升代码的一致性;
  4. 一旦提取出来,我们可以把散落在各个地方对这个数据的同样操作提取为函数。

移除标记参数

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);
}
相关推荐
y0ungsheep1 天前
CTF中的phar反序列化 [SWPU 2018]SimplePHP
运维·web安全·网络安全·php·代码规范
前端郭德纲6 天前
如何修改远程分支?修改了会影响什么?
git·github·代码规范
帅次7 天前
基于云平台的智能家居管理系统设计与通信协议分析
设计模式·重构·软件工程·软件构建·需求分析·代码规范·设计规范
是Yu欸14 天前
【深度学习代码调试5】标准化数据集:TensorFlow Datasets (TFDS)自动化数据加载与预处理
人工智能·python·深度学习·tensorflow·代码规范·1024程序员节
稍敛风翼15 天前
【知识】正反例分析面向对象的七大设计原则(超详细)
c++·代码规范·1024程序员节
PetterHillWater21 天前
GitLab集成GPT进行自动化CodeReview实战
后端·gitlab·代码规范
JavaGuide24 天前
深信服后端开发岗校招面经,挂在了二面!
分布式·哈希算法·线程池·代码规范·分布式id·系统设计·虚拟线程·加密算法·rdb·密码加密·guice
雨绸缪1 个月前
ABAP 的 “小技巧 ”和 “陷阱 ”以及新语法
后端·代码规范·掘金·金石计划
龙智DevSecOps解决方案1 个月前
Perforce静态分析工具2024.2新增功能:Helix QAC全新CI/CD集成支持、Klocwork分析引擎改进和安全增强
linux·安全·ci/cd·代码规范·perforce