首页 > 代码库 > 重构笔记——移除对参数的赋值

重构笔记——移除对参数的赋值

本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42497857


        在上一篇文章中介绍了“分解临时变值“。本文将介绍“移除对参数的赋值”这种重构手法。

        下面让我们来学习这种重构手法吧。


开门见山

        发现:代码对一个参数进行赋值。

        解决:以一个临时变量取代该参数的位置。

	//重构前
	int dicount(int inputVal, int quantity, int yearToDate){
		if(inputVal > 50) inputVal-=10;
	}
	//重构后
	int dicount(final int inputVal, int quantity, int yearToDate){
		int result = inputVal;
		if(result > 50) result-=10;
	}


动机

        我想你很清楚“对参数赋值”这个说话的意思。如果把一个名称为fool的对象作为参数传递给某个函数,那么“对参数赋值”意味改变fool,使它引用另一个对象。但是,如果在“被传入对象”身上进行什么操作,那没问题,我们经常会这么做。这里只针对“fool被改变而指向另一个对象”这种情况来讨论:

void test(Object fool){
	fool.changedBySomeWay(); //that's ok
	fool=anotherObject; //trouble will appear
}
        我们之所不这样做,是因为它降低了代码的清晰度,而且混用了按值传递和按引用传递这两种参数传递方式。JAVA只采用按值进行传递。

        在按值传递的情况下,对参数的任何修改,都不会对调用端造成任何影响。如果你只以参数表示“被传递进来的东西”,那么代码会清晰的多,因为这种用法在所有语言中都表现出相同的语义。

        在JAVA中,一般不要对参数赋值:如果你看到手上的代码已经这么做了,就应该使用本文的方法。



做法

(1)建立一个临时变量,把待处理的参数值赋赋予它。
(2)以“对参数的赋值”为界,将其后所有对此参数的引用点,全部替换为“对此临时变量的引用”。
(3)修改赋值语句,使其改为对新建之临时变量赋值。
(4)编译,测试。
(如果代码的语义是按照引用传递的,需在调用端检查调用后是否还使用了这个参数。也要检查有多少个按引用传递的参数被赋值后又被使用。应该尽量以return方式返回一个值。如果返回值有多个,可考虑将需返回的一大堆数据变为对象,或者为每个返回值设定一个独立的函数)


示例

        我们从一个简单代码开始:
	int dicount(int inputVal, int quantity, int yearToDate){
		if(inputVal > 50) inputVal-=5;
		if(quantity > 100) quantity-=10;
		if(yearToDate > 1000) yearToDate-=100;
		return inputVal;
	}
        以临时变量取代对参数的赋值动作,得到下列代码:
	int dicount(int inputVal, int quantity, int yearToDate){
		int result  = inputVal;
		if(result > 50) result-=5;
		if(quantity > 100) quantity-=10;
		if(yearToDate > 1000) yearToDate-=100;
		return result;
	}
        可以为参数加上final关键词,强制其遵循“不对参数赋值”这一惯例:
	int dicount(final int inputVal, final int quantity, final int yearToDate){
		int result  = inputVal;
		if(result > 50) result-=5;
		if(quantity > 100) quantity-=10;
		if(yearToDate > 1000) yearToDate-=100;
		return result;
	}


JAVA的按值传递

        我们应该都知道,JAVA使用按值传递的函数调用方式,这常常也会使大家迷惑。在所有地点,JAVA都会遵循严格按值传递:

	//JAVA按值的传递
	class Params{
		public static void main(String[] args) {
			int x = 10;
			triple(x);
			System.err.println("x after triple:" + x);
		}
		
		private static void triple(int arg){
			arg = arg * 3;
			System.err.println("arg in triple:" +arg );
		}
	}
	//输出
	//arg in triple:30 
	//x after triple:10
        上面代码是使用基本数据类型进参数传递,还不至于让人糊涂。但如果参数中传递的是对象,就可能把人弄糊涂。如果在程序中以Date对象表示日期,下列程序所示:

//以对象为参数
class Params{
	public static void main(String[] args) {
		Date d1 = new Date(2015,1,1);
		nextDateUpdate(d1);
		System.err.println("d1 after nextday:" + d1);
		
		Date d2 = new Date(2015,1,1);
		nextDateReplace(d2);
		System.err.println("d2 after nextday:" + d2);//61380864000000
		
	}

	private static void nextDateUpdate(Date d) {
		d.setDate(d.getDate()+1);
		System.err.println("arg in nextday d1 : "+d);
	}

	private static void nextDateReplace(Date d) {
		d = new Date(d.getYear(),d.getMonth(),d.getDate()+1);
		d=null;
		System.err.println("arg in nextday d2: "+d);
	}
}

//输出
/*
 arg in nextday d1 : Tue Feb 02 00:00:00 CST 3915
 d1 after nextday:   Tue Feb 02 00:00:00 CST 3915
 arg in nextday d2:  Tue Feb 02 00:00:00 CST 3915
 d2 after nextday:   Mon Feb 01 00:00:00 CST 3915
 */
        从本质上说,对象的引用是按值传递的。因为可以修改参数对象的内部状态,但对参数对象重新赋值是没有意义的。

        本文主要介绍了重构手法——移除对参数的赋值。我觉得这个重构手法使用的情况并不常见,一般情况下,我们都觉得没有必要把参数的值重新定义。但是为了让代码显得比较清晰,在某些情况下还是值得做的。
        最后,希望本文对你有所帮助。有问题可以留言,谢谢。(PS:下一篇将介绍重构笔记——以函数对象取代函数)


重构笔记文章如下

       重构笔记——入门篇

       重构笔记——代码的坏味道(上)

       重构笔记——代码的坏味道(下)

       重构笔记——构筑测试体

       重构笔记——提炼函数

       重构笔记——内联函数

       重构笔记——内联临时变量

       重构笔记——以查询取代临时变量

       重构笔记——引入解释性变量

       重构笔记——分解临时变量


重构笔记——移除对参数的赋值