10 个回答
是时候祭出这本我珍藏多年的武林秘籍了:
让我们来看看它讲了些什么:
第一式:删除
这话听起来不可思议:函数体中已经用到的形参,还怎么删除啊?
但既然书里已然这么写了,编也要编两条方法出来。
强编方法 1.从其他途径获得形参
如有一函数
public myFunction(int var1, int var2, int var3) {
}
- 仔细分析参数的获取方式、参数间的关系;
- 其中某些形参能由别的办法获取,如 f(var1, var3) => var2;
- 且 f 的推导代价(从运行时间、从代码逻辑两方面)不大;
则可以考虑在函数体内获取参数var2。重写后的代码为:
public myFunction(int var1, int var3) {
int var2 = getVar2(var1, var3);
private int getVar2(int var1, int var3) {
//f(var1, var3) => var2的代码逻辑
}
强编方法 2.拆分函数
一个函数拥有大量的参数,有理由怀疑它具有如下特征:
- 函数做了很多事情;
- 各个参数被分散的用到了函数体的各个角落;
这两点特征换一种表达方式:
在函数体中的函数的形参,有一些用在一起,为这个函数实现一些功能。而另外一些聚在一起,为该函数实现别的一些功能。
我们可以从2个维度来分析这种函数:
- 为函数取一个名字,描述函数的功能。是否可以得到形如getXXAndSetXX这种函数名称?
- 在函数体中,一些参数出现的位置,是否比另一些参数显得更为靠近?
这种函数的结构大约为这样:
public myFunction(int var1, int var2, int var3, int var4) {
//下面一段逻辑,使用var1,var2获取x
// 下面一段逻辑,使用上面获得的x, 加上参数var3, var4为y赋值
public static void main(String[] args) {
int arg1 = ...;
int arg2 = ...;
int arg3 = ...;
int arg4 = ...;
myFunction(arg1, arg2, arg3, arg4);
}
这种情况可以这么来减少参数:
-
以形参的聚合紧密度为标准,把函数体中的代码逻辑分段;
-
每一段代码用写成一个子函数;
- 调用原函数的地方,改为调用一个个新作的子函数;
这样我们可以将函数改造为:
public mySubFunction1(int var1, int var2) {
//下面一段逻辑,使用var1,var2获取xx
public mySubFunction2(int middleTmp, int var3, int var4) {
// 下面一段逻辑,使用middleTmp, var3, var4为xx赋值
public static void main(String[] args) {
int arg1 = ...;
int arg2 = ...;
int arg3 = ...;
int arg4 = ...;
int temp = mySubFunction1(arg1, arg2);
mySubFunction2(temp, arg3, arg4);
↓相较于android复杂的系统控制按钮,iPhone上只一个小圆点。
简化参数最明显的方式
就是删除不必要的参数。
第二式:组织
将参数组织起来,并不能从本质上减少参数的个数。但它能使函数的结构简明,并更容易寻找到优化和重构的空间。
常见的 组织 参数的方式,就是将相关联的参数写成一个java bean,或是c struct。
例如一个描绘点运动轨迹的函数:
public void drawOrbit(int x, int y, int time, int velocity){
}
很明显x和y成双成对出现的场合比较多。将它们组织起来,无论是业务层面,还是代码层次上都是不错的简化。
通过组织的方式,将代码简化为:
public void drawOrbit(Coordinate coordinate, int time, int velocity) {
class Coordinate() {
int x;
int y;
Coordinate() {
this.x = x;
this.y = y;
int getX() {
return x;
int getY() {
return y;
}
把坐标参数组织成类后,我们还可以遍历与坐标相关的代码逻辑,基于下述两点做进一步的代码重构:
-
取x、y的属性(或属性的变形)值的代码逻辑 -> 调用Coordinate的相关方法;
-
与x、y有关的重复逻辑多次出现 - > 提取重复代码成函数,将函数作为Coordinate类的一个公用方法
↓将相似功能的设置项组织到一起,用section分割,使得设置选项变得多而不乱,易于用户查找。
组织往往是简化参数
的最快捷方式。
第三式: 隐藏
像题主举例,在不同的函数中,相同的变量作为形参,是一种重复和繁琐的方式。
这种情况下,若var1, var2, ...,var20的形参获取逻辑较复杂,可以考虑使用Hash表来存储形参。
/*假设:f(x1) -> var1;f(x2) -> var2;...f(x20) -> var20;
则我们在函数所在类中定义一个map成员变量,利用key-value的方式获取上述变量。*/
public class Foo{
//定义一个变量map
private final Map<String, Integer> paramMap = new HashMap<String, Integer>();
//在构造函数中初始化变量map
public Foo(int x1, int x2, ... int x20) {
//这里是f(x1) -> var1的代码实现
Integer var1 = ...;
//这里是f(x20) -> var20的代码实现
Integer var20 = ...;
paramMap.put("var1", var1);
paramMap.put("var20", var20);
int myfun1 (){
int tempVar1 = paramMap.get("var1");
int tempVar20 = paramMap.get("var20");
}
有很多种方式,可以让函数体代码获得变量,传递形参是一种显式的方式。其他 隐藏方式有:全局变量、成员变量、配置文件、数据库、redis等缓存技术、session变量等。
隐藏不是一劳永逸的办法,在打算这么干之前,必须好好的评估下面2个负面影响:
-
这么做可能会将变量暴露给不需要的类方法;
-
这么做会让变量变得不可控。如果某些地方意外修改了变量,真正调用时就会使用不正确的"形参值";
↓在wuli键盘还流行的年代,google g1以隐藏式键盘的设计,兼顾了传统与美观,功能与简捷。
隐藏部分参数
是一种低成本的方案。
第四式:转移
所谓转移,是指将传递参数的重任,转移到对形参更熟悉的函数、与形参更亲密的类来完成。从而在上层调用时,达到减少参数的目的。
1.下层函数才暴露参数
例如一个对象有多种创建方式,每种创建方式用到的参数各不相同。则可定义一个基础构造函数,用所有参数的全集,作为该构造函数的形参。其余的构造函数, 调用该基础构造函数 。
public class Foo {
//基础构造函数
public Foo(Integer var1, Integer var2, Integer var3, Integer var4) {
//其余构造函数,调用该基础构造函数,形成构造函数链
public Foo(Integer var1, Integer var3, Integer var4) {
this(var1, null, var3, var4);
public Foo(Integer var1, Integer var2, Integer var4) {
this(var1, var2, null, var4);
}
再如有一些函数,需要传入一个标志位,根据标志位来做不同的业务处理。
例如:
public Response createResponse(HttpServeletRequest request, boolean isSuccessful) {
if(isSuccessful) {
//创建一个代表成功的Response
} else {
//创建一个代表失败的Response
}
这种情况下,将成功和失败 分别写成一个函数,既减少了传入参数 ,又使得函数所做的事更单一、纯粹。
public Response createSuccessResponse(HttpServeletRequest request) {
//创建一个代表成功的Response
public Response createFailureResponse(HttpServeletRequest request) {
//创建一个代表失败的Response
}
2.类来传递函数
假设A类里有一个方法myFunctionInA,它用到了不少的形参,其中大部分都来自另一个类B。那么我们调用的方式很可能是这样:
public class A {
public void myFunctionInA(int var1InB, int var2InB, int var3InB, int otherVar) {
public static void main(String[] args) {
A a = new A();
B b = new B();
a.myFunctionInA(b.getVar1InB(), b.getVar2InB(), b.getVar3InB(), otherVar);
public class B {
private int var1InB;
private int var2InB;
private int var3InB;
public int getVar1InB() {
public int getVar2InB() {
public int getVar3InB() {
}
我们将对myFunctionInA的调用,转移到类B中:
public class A {
public void myFunctionInA(int var1InB, int var2InB, int var3InB, int otherVar) {
public static void main(String[] args) {
A a = new A();
B b = new B();
//下面的函数调用参数减少了!
b.myFunctionInA(a, otherVar);
public class B {
private int var1InB;
private int var2InB;
private int var3InB;
public int getVar1InB() {
public int getVar2InB() {
public int getVar3InB() {