重构函数-Remove Assignments to Parameters移除对参数的赋值动作七
重构函数-Remove Assignments to Parameters移除对参数的赋值动作七
1.移除对参数的赋值动作
1.1.使用场景
这个构造手法使用场景和Java的传参机制有关系,因此在介绍它使用场景前必须先介绍清楚Java传参机制才好理解它的使用场景。
1.Java传参机制定论
Java传参使用在函数的参数上,定义函数参数就是形参,调用函数传递参数就是实参。因此要问问调用函数传递的参数是值传递,还是引用传递?
Java的设计是值传递,没有引用传递
2.值传递和引用传递区别
值传递(pass by value)
是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)
是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
3.Java是值传递论点
通常在理解Java参数传递时会有个误解:传递的参数如果是基本类型,那就是值传递,如果是对象,那就是引用传递。
Java基本类型值传递论点
public class NoLocalVariables {
public static void change(int i) {
i = 20;
System.out.println("after change i = " + i);
}
public static void main(String[] args) {
int i = 10;
change(i);
System.out.println("before change i = " + i);
}
}
在示例中定义了一个基本类型int的变量,然后在change()
方法中修改其值。分别输出修改后和修改前i
的值,运行代码看下结果。
after change i = 20
before change i = 10
修改后的值没有影响修改前的值,说明java是将i
的值复制了一份传给change()
方法。结论基本类型是值传递。
Java引用类型值传递论点
class User {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class NoLocalVariables {
public static void change(User s) {
s.setName("李四");
System.out.println("after change s = " + s.getName());
}
public static void main(String[] args) {
User s = new User();
s.setName("张三");
//将对象引用地址复制一份传递给方法
change(s);
System.out.println("before change s = " + s.getName());
}
}
创建一个User
类对象表示引用类型,调用change()
方法,此时Java将User
对象引用地址复制了一份作为参数传递给change()
方法,由于复制的地址都指向同一个对象,所以修改User
对象值后该值改变了。
after change s = 李四
before change s = 李四
Java常量类型值传递论点
在Java中String
类优点特殊,他是一个常量类。通过下面的源码可以看出它的存放数据是一个常量char
类型的数组,所以它的数据是存放到常量池。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
通过字面量方式创建String对象,
public class NoLocalVariables {
public static void change(String s) {
s = "s2";
System.out.println("after change s = " + s);
}
public static void main(String[] args) {
String s = "s1";
change(s);
System.out.println("before change s = " + s);
}
}
将String对象作为参数传入change
方法后,修改它的值。因为字符串值是存在常量区,而常量区中相同的值只创建一次,当值不相同时就会在常量池中创建一个地址存放值,因此两个值不相同。
after change s = s2
before change s = s1
4.移除对参数的赋值动作使用场景
介绍完Java参数传递方式后,再来介绍使用场景就简单了。
当我们对参数做赋值操作后就改变了foo,然后又将另一个对象赋值给foo。造成 pass by value(传值〕和pass by reference (传址)这两种参数传递方式混淆,降低了代码的清晰度。
void aMethod(Object foo) {
// 修改foo对象值
foo.modifyInSomeWay();
// 将foo指向另一个对象
foo = anotherObject;
1.2.如何使用
- 建立一个临时变量,把待处理的参数值赋予它。
- 以「对参数的赋值动作」为界,将其后所有对此参数的引用点,全部替换为「对此临时变量的引用动作」。
- 修改赋值语句,使其改为对新建之临时变量赋值。
- 编译,测试。
- 如果代码的语义是 pass by reference,请在调用端检查调用后是否还使用了这个参数。
- 也要检查有多少个 pass by reference参数「被赋值后又被使用」。
- 请尽量只以return方式返回一个值。
- 如果需要返回的值不只一个,看看可否把需返回的大堆数据变成单一对象,或千脆为每个返回值设计对应的一个独立函数。
1.3.示例
int discount (int inputVal, int quantity, int yearToDate) {
if (inputVal > 50) inputVal -= 2;
if (quantity > 100) inputVal -= 1;
if (yearToDate > 10000) inputVal -= 4;
return inputVal;
}
以临时变量取代对参数的赋值动作,得到下列代码
int discount (int inputVal, int quantity, int yearToDate) {
int result = inputVal;
if (inputVal > 50) result -= 2;
if (quantity > 100) result -= 1;
if (yearToDate > 10000) result -= 4;
return result;
}