【重构】Long Method(过长函数)

场景演义

场景一 函数过长,难以理解,不知道做什么,如何做

重构手法

Extract Method(抽取公共代码)

场景二 函数内有大量参数与临时变量

重构手法

Replace Temp with Query(以查询取代临时变量)

程序以一个临时变量保存某一表达式的运行结果,将这个表达式提炼到一个独立的函数中,将这个临时变量所有的引用点替换为对新函数的调用。此后新函数就可被其他函数使用。

范例:重构前

double basePrice=_quantity*itemPrice;
if(basePrice>1000){
	return basePrice*0.95;
}else{
	return basePrice*0.98;
}

范例:重构以后
if(getBasePrice()>1000){
	return getBasePrice()*0.95;
}else{
	return getBasePrice()*0.98;
}

public double getBasePrice(){
	return _quantity*itemPrice;
}

动机:临时变量是暂时的,只能在所属函数内使用,由于临时变量只在所属函数内可见,所以它会驱使你写出更长的函数,因为只有这样你才能访问到需要的临时变量。

范例:

double getPrice(){
	int basePrice=_quantity*_itemPrice;
	double discountFactory;
	if(basePrice>1000){
		discountFactory=0.98;
	}else{
		discountFactory=0.95;
	}
	return basePrice*discountFactory;
}

步骤一 先把临时变量申明为final类型的 方便做整体的替换 避免遗漏
double getPrice(){
	final int basePrice=_quantity*_itemPrice;
	final double discountFactory;
	if(basePrice>1000){
		discountFactory=0.98;
	}else{
		discountFactory=0.95;
	}
	return basePrice*discountFactory;
}

步骤二 使用replace temp with query 先把临时变量替换掉
 double getPrice(){
	final int basePrice=
	final double discountFactory;
	if(basePrice>1000){
		discountFactory=0.98;
	}else{
		discountFactory=0.95;
	}
	return basePrice*discountFactory;
}

public int getBasePrice(){
	return _quantity*_itemPrice;
}

public double getFactory(){
	final double discountFactory;
	if(getBasePrice()>1000)
	  return discountFactory=0.98;
	return discountFactory=0.95;
}

步棸三 把函数中的临时变量替换为函数
double getPrice(){
	return getBasePrice()*getFactory();
}

public int getBasePrice(){
	return _quantity*_itemPrice;
}

public double getFactory(){
	final double discountFactory;
	if(getBasePrice()>1000)
	  return discountFactory=0.98;
	return discountFactory=0.95;
}

Introduce Parameter Object(引入参数对象)

某些参数总是很自然的同时出现,以一个对象取代这些参数

范例

重构前

动机:你常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用了这一组参数,这些函数可能率属于同一个类,也可能率属于不同的类,这样一组参数就是所谓的的 Data Clumps(数据泥团),我们可以用一个对象包装所有的这些数据,再以该对象取代它们。

public class A{
	
	public void calcultorM(Date start,Date end){
		System.out.println(start.getTime()-end.getTime());
	}
}

public class B{

	public void calculatorMC(Date start,Date end){
		System.out.println(start.getTime()+end.getTime());
	}	
}

步骤一 先添加一个日期范围类 准备把过长或是,在一定范围内的参数用类封装起来表示
public class DateRange{
	private Date start;
	private Date end;
	
	public Date getStart(){
		return start;
	}
	
	public Date getEnd(){
		return end;
	}
	
	public void setStart(Date start){
		this.start=start;
	}
	
	public void setEnd(Date end){
		this.end=end;
	}
}

步骤二 用类变量取代参数

public class A{
	
	public void calcultorM(DateRange datarange){
		System.out.println(datarange.getStart().getTime()-datarange.getEnd().getTime());
	}
}

public class B{

	public void calculatorMC(DateRange datarange){
		System.out.println(datarange.getStart().getTime()+datarange.getEnd().getTime());
	}	
}


public class DateRange{
	private Date start;
	private Date end;
	
	public Date getStart(){
		return start;
	}
	
	public Date getEnd(){
		return end;
	}
	
	public void setStart(Date start){
		this.start=start;
	}
	
	public void setEnd(Date end){
		this.end=end;
	}
}
重构完成。

Preserve Whole Object(保持对象完整)

你从某个对象中取出若干值,将他们作为某一次函数调用时的参数,改为传递整个对象

范例

int low =daysTempRange().getLow();
int high=daysTempRange().getHigh();
withinPlan=plan.withinRange(low,high);

withinPlan=plan.withinRange(daysTempRange());

动机

将来自同一个对象的若干数据作为参数,传递给某个函数,这样做的问题在于;万一将来被调用的函数需要数据项,你就必须查找和修改此函数的所有调用,如果你把这些数据作为整个对象传递,就可以便面这种尴尬的处境,因为被调用的函数可以向那个参数对象请求任何它想要的信息。

Replace Method with Method Object(以函数对象取代函数)

有一个大型的函数,对局部变量的使用无法采用Extracted Mehod,将这个函数放进一个单独的对象中,如此一来局部变量就成了对象内的字段,然后你可以在同一个对象中将这个大型函数分解为多个小型函数。

动机

局部变量的存在,会增加函数分解的难度,有些时候你会发现,局部变量更本无法拆解成为一个函数,这时你应该把这些局部变量,变成对象的字段,然后再抽取成为函数。

范例

范例

Class Account{
	
	int gamma(int inputVal,int quantity,int yearToDate){
		int importantValue1=(inputVal*quantity)+delta();
		int importantValue2=(inputVal * yearToDate) +100;
		if((yearToDate-importantValue1)>100){
			importantValue2-=20;
		}
		int  importantValue3=importantValue2*7;
		return importantValue3 -2 * importantValue1;
	}
}

如上代码段所示,里面有大量的局部变量

我们可以使用replace method with method Object

这个方法

步骤一先构建一个含有这些局部变量的类,然后把原有的方法复制过来

class Gamma{
	private final Account _account;
	private int inputVal;
	private int quantity;
	private int yearToDate;
	private int importantValue1;
	private int importantValue2;
	private int importantValue3;
	
	public Gamma(Account account,int inputValArg,int quantity,int yearToDate){
		this._account=account;
		this.inputVal=inputValArg;
		this.quantity=quantity;
		this.yearToDate=yearToDate;
	}
	
	int compute(){
		int importantValue1=(inputVal*quantity)+_account.delta();
		int importantValue2=(inputVal * yearToDate) +100;
		if((yearToDate-importantValue1)>100){
			importantValue2-=20;
		}
		int  importantValue3=importantValue2*7;
		return importantValue3 -2 * importantValue1;
	}
}

步骤二 用现有的方法替换原有的方法
Class Account{
	
	int gamma(int inputVal,int quantity,int yearToDate){
		return new Gamma(this,inputVal,quantity,yearToDate).compute();
	}
}

场景三 大量的条件表达式与循环

重构手法

Decompose Conditional(分解条件表达式)

范例

重构前

if(date.before(SUMMER_START)||date.after(SUMMER_EEND)){
	charge=quantity*_winterRate*_winterServiceCharge;
}else{
    charge=quantity *_summerRate;
}

重构以后
if(notSummer(date)){
	charge= winterCharge(quantity);
}else{
    charge= summerCharge(quantity);
}

动机:程序中,复杂的条件逻辑是最常导致复杂度上升的地点之一。你必须编写代码来检查不同的条件分之,根据不同的分支做不同的事,然后你会得到一个相当长的函数。大型函数自身就会使代码的可读性下降,而条件逻辑则会使代码更难阅读。

完成范例

if(date.before(SUMMER_START)||date.after(SUMMER_EEND)){
	charge=quantity*_winterRate*_winterServiceCharge;
}else{
    charge=quantity *_summerRate;
}

重构以后
if(notSummer(date)){
	charge= winterCharge(quantity);
}else{
    charge= summerCharge(quantity);
}


private boolean notSummer(){
  return date.before(SUMMER_START)||date.after(SUMMER_EEND);
}

private double summerCharge(){
  return quantity*_summerRate;
}

private double winterCharge(){
  return quantity* _winterRate*_winterServiceCharge;
}


 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值