《Refactoring》,Remove Assignments to Paraments(移除对参数的赋值)

开门见山
发现:代码对一个参数进行赋值。
解决:以一个临时变量取代该参数的位置。

//重构前
    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
 */

从本质上说,对象的引用是按值传递的。因为可以修改参数对象的内部状态,但对参数对象重新赋值是没有意义的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值