extract method(提炼函数)

你有一段代码可以被组织在一起并提炼出来

// 提炼前
void printOwning(double amount){
  printBananer();
  
  // print details
  Sysout.out.println("name:" + _name);
  Sysout.out.println("amount:" + amount);
}

// 提炼后
void printOwning(double amount){
  printBananer();
  printDetails(amount)
}

void printDetails(double amount){
  Sysout.out.println("name:" + _name);
  Sysout.out.println("amount:" + amount);
}

动机

  • 函数越小可读性越高

  • 函数越小复用的机会就越大

可读性高的前提是能给函数取一个好名字

根据名字就可以直接明白函数是干啥的,而不需要跳转进去

需要一定的词汇量并且会归纳

做法

  1. 取一个好名字

我们提炼的目的是增加代码的可读性,如同看文章一样,见到函数的名就知道它是做什么的,而不用再去关心它是怎么做的,提高可读性。

所以需要以函数做什么命名,而不是怎么做

举例:

查询订单的失效时间后,与当前时间判断,早于当前时间则订单失效,晚于当前时间则订单有效。

这里我们做什么?判断订单是否有效

所以提炼的方法名应为:isOrderEffective(),而不是compareWithNowTime()

前者是做什么,后者是怎么做

  1. 提炼代码从源函数复制到新建的目标函数中
  1. 检查变量,有没有用到源函数的局部变量和参数;

这里的目的是要看我们提炼的函数产生的影响,会不会受源函数的影响。

  1. 检查变量,有没有只在目标函数中使用的变量,如果有的话,在目标函数中声明为临时变量
  1. 检查被提炼代码段,看看有没有局部变量的值被它改变。

如果有一个临时变量被修改了,看看能否将被提炼代码段处理为一个查询,并将结果赋值给相关变量。

如果被修改的变量有点多了,就要先拆解变量,再来单独提炼。

  1. 将被提炼代码段中需要读取的局部变量,当做参数传给目标函数
  2. 处理完所有局部变量,编译
  3. 在源函数中,将被提炼代码段替换为对目标函数的调用,删除未使用的临时变量声明
  4. 编译、测试

范例

// 提炼前
void printOwning(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;
  
  // print banner
  Sysout.out.println("**********************");
  Sysout.out.println("****Customer Ownes****");
  Sysout.out.println("**********************");
  
  // calculate outstanding
  while(e.hasMoreElements()){
    Order each = (Order)e.nextElement();
    outstanding += each.getAmount();  
  }
  
  // print details
  Sysout.out.println("name:" + _name);
  Sysout.out.println("amount:" + outstanding);
}
// 1. 无局部变量,提炼函数 printBanner()
void printOwning(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;
  
	printBanner();
  
  // calculate outstanding
  while(e.hasMoreElements()){
    Order each = (Order)e.nextElement();
    outstanding += each.getAmount();  
  }
  
  // print details
  Sysout.out.println("name:" + _name);
  Sysout.out.println("amount:" + outstanding);
}

void printBanner(){
  Sysout.out.println("**********************");
  Sysout.out.println("****Customer Ownes****");
  Sysout.out.println("**********************");
}


// 2. 使用到了源函数的局部变量且为只读,将源函数具备变量传入目标函数
void printOwning(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;
  
	printBanner();
  
  // calculate outstanding
  while(e.hasMoreElements()){
    Order each = (Order)e.nextElement();
    outstanding += each.getAmount();  
  }
  
  printDetails(outstanding);
  	
}

void printDetails(double outstanding){
  Sysout.out.println("name:" + _name);
  Sysout.out.println("amount:" + outstanding);
}

// 3. 对局部变量再赋值:使用源函数局部变量,并且进行了操作
void printOwning(){
	printBanner();
 	double outstanding=getOutStanding();
  printDetails(outstanding);
}

double getOutStanding(){
   Enumeration e = _orders.elements();
   double outstanding = 0.0;
   while(e.hasMoreElements()){
    Order each = (Order)e.nextElement();
    outstanding += each.getAmount();  
  }
  return outstanding;
}

// 4. 回传值改名
double getOutStanding(){
   Enumeration e = _orders.elements();
   double result = 0.0;
   while(e.hasMoreElements()){
    Order each = (Order)e.nextElement();
    result += each.getAmount();  
  }
  return result;
}

上面的情况中 getOutStanding 是没有入参的,因为源函数只是对 outstanding 做了初始化,而没有做其他操作,如果源函数中对 outstanding 做了其他的操作,就需要将 outstanding 作为入参传入。

// 提炼前
void printOwning(double previousAmount){
  Enumeration e = _orders.elements();
  double outstanding = previousAmount * 1.2;
  
	printBanner();
  
  // calculate outstanding
  while(e.hasMoreElements()){
    Order each = (Order)e.nextElement();
    outstanding += each.getAmount();  
  }
  
  printDetails(outstanding);
  	
}

// 1. 提炼之后,将操作后的 局部变量 作为入参传入目标函数
void printOwning(double previousAmount){
  double outstanding = previousAmount * 1.2;
	printBanner();
  outstanding = getOutStanding(outstanding)
  printDetails(outstanding);
}

double getOutStanding(double initialValue){
   double result = initialValue;
   Enumeration e = _orders.elements();
   while(e.hasMoreElements()){
    Order each = (Order)e.nextElement();
    result += each.getAmount();  
  }
  return result;
}

// 2. 去除临时变量,优化outstanding初始化过程
void printOwning(double previousAmount){
	printBanner();
  double outstanding = getOutStanding(previousAmount * 1.2)
  printDetails(outstanding);
}
上一篇:Solution -「CF 600A」Extract Numbers


下一篇:第三章_JSP