Long Parameter List(过长参数列)---要重构的味道

一个函数,它的参数过多是不好的,不好维护和修改,易读性也差,容易出错。

消除过长参数的方法,有如下:

1.在面向对象中,你可以传递一个对象给函数,函数通过访问对象来获得参数。也就是,对象里面了包含参数需要的多个参数。

2.函数通过访问函数所在类的成员变量,或者其它函数来获取原来要传入的参数。因为,有时候,是可以自己通过宿主类来获取需要的值,而不需要外部传入。

但是,并不是在任何情况下,都包装所有参数到一个对象中。有时候,是反过来,把参数从对象中拆解出来,因为,你不想要对象间的依赖。

相应的重构手法:

 Replace Parameter with Method(用函数取代参数)

手法的含义:对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。那么,让参数接受者去除该项参数,并直接调用前一个函数。

例子:

public double getPrice() {

       int basePrice = _quantity * _itemPrice;
       int discountLevel;
       if (_quantity > 100) discountLevel = 2;
       else discountLevel = 1;
       double finalPrice = discountedPrice (basePrice, discountLevel);
       return finalPrice;
   }

   private double discountedPrice (int basePrice, int discountLevel) {
       if (discountLevel == 2) return basePrice * 0.1;
       else return basePrice * 0.05;
   }

应用Replace Parameter with Method手法,重构后,结果是:将discountLevel参数消除掉,通过在函数体内添加参数值获取函数来实现。

public double getPrice() {

       return discountedPrice ();
   }

   private double discountedPrice () {
       if (getDiscountLevel() == 2) return getBasePrice() * 0.1;
       else return getBasePrice() * 0.05;
   }

   private double getBasePrice() {
       return _quantity * _itemPrice;
   }

  Preserve Whole Object(保持对象完整)

手法的含义:你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。改为传递整个对象。

例子:

重构前:

HeatingPlan方法withinRange(int low, int high)。

我们的目的是,把这两个参数才为一个,修改为从从一个对象中获取。

  class Room...
    boolean withinPlan(HeatingPlan plan) {
        int low = daysTempRange().getLow();
        int high = daysTempRange().getHigh();
        return plan.withinRange(low, high);
    }
  class HeatingPlan...
    boolean withinRange (int low, int high) {
        return (low >= _range.getLow() && high <= _range.getHigh());
    }
    private TempRange _range;

修改后的效果:从TempRange对象获取值。

class HeatingPlan...

    boolean withinRange (TempRange roomRange) {
        return (roomRange.getLow() >= _range.getLow() && roomRange.getHigh() <= _range.getHigh());
    }
  class Room...
    boolean withinPlan(HeatingPlan plan) {
        int low = daysTempRange().getLow();
        int high = daysTempRange().getHigh();
        return plan.withinRange(daysTempRange());
    }

再调整一下,因为_range也是一个TempRange类型的对象。在TempRange中添加是否包含范围的方法。

效果为:

class HeatingPlan...

    boolean withinRange (TempRange roomRange) {
        return (_range.includes(roomRange));
    }
  class TempRange...
    boolean includes (TempRange arg) {
        return arg.getLow() >= this.getLow() && arg.getHigh() <= this.getHigh();

}

Introduce Parameter Object(引入参数对象)

手法的含义:

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

例子:

class Account...

    double getFlowBetween (Date start, Date end) {
        double result = 0;
        Enumeration e = _entries.elements();
        while (e.hasMoreElements()) {
            Entry each = (Entry) e.nextElement();
            if (each.getDate().equals(start) ||
                each.getDate().equals(end) ||
                 (each.getDate().after(start) && each.getDate().before(end)))
            {
                result += each.getValue();
            }
        }
        return result;
    }
    private Vector _entries = new Vector();

  client code...

double flow = anAccount.getFlowBetween(startDate, endDate);

参数start,end是成对出现的。可以用一个对象来代替它们

class DateRange {

    DateRange (Date start, Date end) {
        _start = start;
        _end = end;
    }
    Date getStart() {
        return _start;
    }
    Date getEnd() {
        return _end;
    }
    private final Date _start;
    private final Date _end;
  }

最终修改后,效果如下:

范围确定操作,被迁移到进行范围过程中用到的参数所在的类中。

class Account...

    double getFlowBetween (DateRange range) {
        double result = 0;
        Enumeration e = _entries.elements();
        while (e.hasMoreElements()) {
            Entry each = (Entry) e.nextElement();
            if (range.includes(each.getDate())) {
                result += each.getValue();
            }
        }
        return result;
    }

  class DateRange...
    boolean includes (Date arg) {
        return (arg.equals(_start) ||
                arg.equals(_end) ||
                 (arg.after(_start) && arg.before(_end)));
    }

参考资料: https://sourcemaking.com/refactoring/preserve-whole-object https://sourcemaking.com/refactoring/replace-parameter-with-method https://sourcemaking.com/refactoring/introduce-parameter-object
时间: 2024-10-12 18:18:59

Long Parameter List(过长参数列)---要重构的味道的相关文章

C++11 新特性之 变长参数模板

template <typename ... ARGS> void fun(ARGS ... args) 首先明确几个概念 1,模板参数包(template parameter pack):它指模板参数位置上的变长参数,例如上面例子中的ARGS 2,函数参数包(function parameter pack):它指函数参数位置上的变长参数,例如上面例子中的args 一般情况下 参数包必须在最后面,例如: template <typename T, typename ... Args>

Java语法糖初探(三)--变长参数

变长参数概念 在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用.形如 function(T -args).但是需要明确的一点是,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组. 看下面的例子: 12345678910111213 public class VariVargs { public static void main(String []args) { tes

Python的不定长参数研究

 通过观察程序和运行结果我们发现,传参时将1传给了a,将2传给了b,将3,4,5传给了*args,将m=6,n=7,p=8传给了**kwargs.为什么是这样传参呢?*args和**kwargs又是什么呢? *args表示任何多个无名参数,它是一个tuple,所以将3,4,5传给了*args: **kwargs表示关键字参数,它是一个dict,所以将m=6,n=7,p=8传给了**kwargs. 并且同时使用*args和**kwargs时,必须*args参数列要在**kwargs前. 下面用代码

介绍C++11标准的变长参数模板

目前大部分主流编译器的最新版本均支持了C++11标准(官方名为ISO/IEC14882:2011)大部分的语法特性,其中比较难理解的新语法特性可能要属变长参数模板(variadic template)了.下面先介绍一下这个语法特性在C++11标准中的描述. 14.5.3 变长参数模板(Variadic templates) 1.一个模板形参包(template parameter pack)是一个接受零个或多个模板实参的模板形参.[例: template<class ... Types> st

Java中可变长参数的使用及注意事项

在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用,例如print("hello");print("hello","lisi");print("hello","张三", "alexia");下面介绍如何定义可变长参数 以及如何使用可变长参数. 1. 可变长参数的定义 使用...表示可变长参数,例如 prin

Scala-变长参数

在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数. def sum(nums: Int*) = { var res = 0 for (num <- nums) res += num res } sum(1, 2, 3, 4, 5) 使用序列调用变长参数 在如果想要将一个已有的序列直接调用变长参数函数,是不对的.比如val s = sum(1 to 5).此时需要使用Scala特殊的语法将参数定义为序列,让Scala解释器能够识别. val s = sum

30、不定长参数

def sum( a, b=10):b是缺省参数  ,向后排 result = a+b  sum( 12, 24 )  sum(a=12 ,  b)  a 是命名参数 def sum( a, b,*args,  * *kwargs ):是不定长参数,args是变量名,*是一个标识,告诉解释器要特殊对待 result = a+b sum(12, 23, 24, 15, m=3,n=5) print(agrs)   ( 24,15) print(kwargs) {'m':3, 'n': 5 } fo

scala学习笔记-变长参数(5)

变长参数 在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数. 1 def sum(nums: Int*) = { 2 var res = 0 3 for (num <- nums) res += num 4 res 5 } 6 7 sum(1, 2, 3, 4, 5) 使用序列调用变长参数 在如果想要将一个已有的序列直接调用变长参数函数,是不对的.比如val s = sum(1 to 5).此时需要使用Scala特殊的语法将参数定义为序列,让Scala解

读书笔记:c语言标准库 - 变长参数

· 变长参数(stdarg.h) 变长参数是c语言的特殊参数形式,例如如下函数声明: int printf(const char * format,...); 如此的声明表明,printf函数除了第一个参数类型为const char*之外,其后可以追加任意数量.任意类型的参数. 在函数实现部分,可以使用stdarg.h里的多个宏来访问各个额外的参数:假设lastarg是变长参数函数的最后一个具名参数(printf里的format),那么在函数内容定义类型为va_list的变量: va_list