欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

重构改善既有代码的设计《六》重新组织函数

程序员文章站 2022-05-06 22:54:04
...

6.1Extract Method(提炼函数)

1,你有一段代码可以被组织在一起并独立出来
将则这段代码房间一个独立函数中,并让函数名称解释该函数的用途

 void  printData(double amount){
        printBanner();
        //print detail
        System.out.println("name:"+_name);
        System.out.println("amount:"+amount);
    }

修改成:

 void printData(double amount) {
        printBanner();
       printDetail(amount);
    }
    void printDetail(double amount) {
        System.out.println("name:" + _name);
        System.out.println("amount:" + amount);
    }

2,动机

  • 如果每个函数的粒度都很小,那么函数复用的机会就很大
  • 这会使高层函数读起来象一系列注释
  • 如果函数都是细颗粒度,那么覆写也会更加容易
    3,做法
  • 创建一个新函数,根据这个函数的意图命名(做什么)
  • 将提炼的代码从原函数复制到新建目标函数中
  • 检查是否有“仅用于被提炼代码段”的临时变量,如果有,在目标函数将它们声明成临时变量
  • 检查被提炼代码段,看看是否有任何局部变量的值被他改变
  • 将被提炼代码段中需要的读取的局部变量,当作参数传给目标函数
  • 处理完所有的局部变量之后,进行编译
  • 在原函数中,将被提炼代码段替换为对目标函数的调用
  • 编译,测试
    4,疑问
  • 如果需要返回的变量不止一个?
    挑选另外一个代码来提炼,尽量每一个函数都只返回一个值

6.2Inline Method (内联函数)

1,一个函数的本体与名称同样清楚易懂
在函数调用点插入函数本体,然后移除该函数

    int getRating() {
        return (moreThanFiveLateDeliveries())?2:1;
    }
    boolean moreThanFiveLateDeliveries(){
        return   _numberOfLateDeliveries>5;
    }

修改为:

int getRating() {
        return (_numberOfLateDeliveries>5)?2:1;
}

2,动机

  • 有时候某些函数,其内部代码和函数名称同样清晰易读
  • 组织不甚合理的函数,可以将他们内联到一个大型函数,再提炼出组织合理的小型函数
    3,做法
  • 检查函数,确定不具备多态性
    如果子类继承了这个函数,就不要将此函数内联,因为子类无法覆写一个根本不存在的函数,
  • 找出这个函数的调用点,
  • 将这个函数的所有的调用点换成函数主体
  • 编译,测试
  • 删除之前的函数

6.3Inline Temp(内联临时变量)

1,你有一个临时变量,只被一个简单的表达式赋值一次,而它妨碍了其他重构手法
将所有对该变量的引用动作,替换为对它赋值的那个表达式自身

  double basePrice=anOrder.basePrice();
  return   basePrice>1000;

修改为

  return   anOrder.basePrice()>1000;

2,动机/做法 (略)

6.4Replace Temp With Query(以查询取代临时变量)

1,你的程序以一个临时变量保存某一表达式的运算结果
将这个表达式提炼到一个独立函数中,将这个临时变量的所有的引用点替换为对新函数的调用,此后,这个新函数就可能被其他的函数引用

  double basePrice = _quanlity * _itemPrice;
        if (base > 1000)
            return basePrice * 0.95;
        else
            return basePrice * 0.7;

修改为:

 double basePrice = _quanlity * _itemPrice;
        if (base > 1000)
            return basePrice() * 0.95;
        else
            return basePrice() * 0.7;

}
double basePrice () {
     return _quanlity * _itemPrice;
 }

2,动机
临时变量的问题:它们是暂时的,而且只能所属函数使用
技巧:如果临时变量替换为一个查询,那么同一个类的所有的函数都可以获取这份信息
3,做法

  • 找出只被赋值一次的临时变量
  • 将该临时变量声明为final
    可确保临时变量的确只是被赋值一次
  • 编译
  • 将“对该临时变量赋值”之语句的等号右侧部分提炼到一个独立函数
    1)首先将函数声明为private
    2)确保提炼出来的函数无任何副作用
  • 编译测试
  • 在该变量实施Inline Temp

6.5Introduce Explaining Variable(引入解释性变量)

1,你有一个复杂的表达式
将该复杂的表达式(或其中一部分)的i俄国放进一个临时变量,以此变量名称来解释表达式用途

if (platform.toUppercase().indexOf("MAC")>-1)&&platform.toUppercase().indexOf("IE")>-1&&wasInitialized()&&resize>0{
            //doSomething
 }

修改为

final  boolean isMacOs=platform.toUppercase().indexOf("MAC")>-1;
final  boolean isIEBrowser=platform.toUppercase().indexOf("IE")>-1;
final  boolean wasResized=resize>0;
if (isMacOs&&isIEBrowser&&wasInitialized()&&wasResized){
 //doSomething
 }

2,动机
在条件逻辑中,引入解释性变量特别有价值,你可以用这项重构将每个条件子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义,使用这项重构的另一种情况,在较长算法中,可用临时白能量来解释每一步运算的意义
3,做法

  • 声明一个final临时变量,将待分解之复杂表达式中的一部分动作的运算结果赋值给他
  • 将表达式中的运算结果这一部分,替换为上述临时变量
    如果被替换的这一部分在代码中重复出现,你可以每次一个,逐一替换
  • 编译测试
  • 重复上述过程,处理表达式的其他部分

4,什么时候使用
在Extract Method 需要胡斐更大工作量的时候,处理一个拥有大量局部变量的算法

6.6Split Temporary Variable(分解临时变量)

1,你的程序某个临时变量赋值超过一次,它既不是循环变量,也不被用于收集计算结果
针对每次赋值,创造一个独立,对应的临时变量

 double temp=2*(_height+_wideth);
 System.out.println(temp);
  temp=_height*_wideth;
  System.out.println(temp);

修改为:

final double perimeter=2*(_height+_wideth);
System.out.println(perimeter);
 final double area=_height*_wideth;
 System.out.println(area);

2,动机
同一个变量承担不同的事情,会令代码阅读者迷惑
循环变量:会随循环次数变化而变的变量
结果收集变量:“通过整个函数的运算"而构成某一个值收集起来
3,做法

  • 在待分解的来闹事变量声明及其第一次被赋值处,修改其名称
  • 将新的临时变量声明为final
  • 以该临时变量的第二次赋值动作为界,修改此前对该临时变量的所有的引用点,让他们引用新的临时变量
  • 在第二次赋值出处,重新声明原先那个临时变量
  • 编译测试
  • 重复

6.7Remove Assignments Parameter(移除对参数的赋值)

1,代码对一个参数进行赋值
以一个临时变量取代该参数的位置

 int  discount(int inputVal,int quantity,int yearToDate){
        if(inputVal>50) inputVal-=2;
    }

修改为:

  int  discount(int inputVal,int quantity,int yearToDate){
        int result=inputVal;
        if(inputVal>50) result-=2;
    }
    还可以为
     int  discount(final int inputVal,final int quantity,final int yearToDate){
        int result=inputVal;
        if(inputVal>50) result-=2;
    }

2,动机
降低代码的清晰度,而且混肴按值传递和按引用传递这两种参数传递方式
在java中,不要对参数赋值
3,做法(略)

6.8Replace Method with Method Object(以函数对象取代函数)

你有一个大型函数,其中对局部变量的使用无法采用Extract Method
将则这个函数放进一个单独的对象中,如此一来局部变量就成了对象内的字段,然后你可以在同一对象中将这个大型函数分解为多个小型函数

class Order...
    double price() {
      double  primaryBasePrice;
      double secndaryBasePrice;
      double  tertiaryBasePrice;
        //long computation
        ....
    }

重构改善既有代码的设计《六》重新组织函数
2,动机
当一个变量中局部变量泛滥的时候,函数对象
3,做法

  • 建立一个新类,根据待处理函数的用途,为此函数命名
  • 在新类中建立一个final字段,用以保存原先大型函数所在的对象,我们将这个对象称为源对象,同时,针对原函数的每一个临时变量和每一个参数,在新类中建立一个对应得字段保存之
  • 在新类中建立一个构造函数,接受源对象及原函数的所有参数为参数
  • 在新类建立一个compute()函数
  • 将原函数的代码复制到compute()函数中,如果需要调用源对象的任何函数,请通过源对象字段diaoyong
  • 编译
  • 将旧函数的函数体替换为这样一条语句“创建上述新类的一个新对象,而后调用其中compute()函数”
    4,补充
    修改旧函数,让它将他的工作委托刚刚完成这个函数对象
    好处:可以轻松对compute函数采取Extract Method,不必担心参数传递问题

6.9Substitute Algorithm(替换算法)

如果你想把某个算法替换为另一个更清晰的算法
将函数本体替换为另一个算法
注意:结果一定要保持相同

相关标签: 重构