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

天大地大妈咪最大 提交于 2020-01-15 18:54:31

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(替换算法)

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!