【《重构 改善既有代码的设计》学习笔记6】重新组织函数

版权声明:本文为博主-阿飞(dufyun)-原创文章,未经博主允许可转载,但请标明出处,谢谢! https://blog.csdn.net/u010648555/article/details/87955081

本篇文章的内容来自《重构 改善既有代码的设计》一书学习笔记整理并且加上自己的浅显的思考总结!

重构手法中,很大一部分是对函数进行整理,使之更恰当地包装代码。

重新组织函数

对过长的函数进行拆解,提炼函数,并处理局部变量,使得拆解后的函数更加清晰并且能够更好的工作。

1、提炼函数(Extract Method)

概要

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

void print(){
    printBanner();
    System.out.print("name:"+ _name);
    System.out.print("age:"+ age);
}
// 修改后
void print(){
    printBanner();
    printDetail();
}
void printDetail(){
     System.out.print("name:"+ _name);
     System.out.print("age:"+ age);
}

动机

提炼函数是最常用的手法之一。将一段独立的代码进行抽离并放入一个独立的函数中,并给这个函数起一个简短而命名良好的名字,这种提炼也可以方便后续的代码的复用。

一个函数多长才算合适?

长度不是问题,关键在于函数名称和函数本体之间的语义距离。

做法

♢创建一个函数,根据这个函数的意图来对它命名。(“做什么”命名,而不是“怎么做”命名)

♢将提炼出的代码从源函数复制新建目标函数中

♢检查是否有需要处理的临时变量

♢处理完成,编译

♢在源函数中,调用提炼的目标函数,编译、测试

【这个重构使用较多,希望一定要能够掌握】

范例

  • 无局部变量

比较简单,剪切、粘贴,插入一个函数即可。

无局部变量

  • 有局部变量

局部变量:包括传入源函数的参数和源函数所申明的临时变量。 **局部变量的作用域仅限于 源函数。**所以需要额外去处理这些变量。

局部变量最简单的情况:被提炼的函数段只是读取这些变量的值,并不修改它们。这种情况可以简单传参数到目标函数。

有局部变量

如果局部变量是个对象,也可以进行参数传递。但是一定要注意,目标函数是否会对对象赋值.

【对象当作参数传递,要了解按值传递和按引用传递的概念和相关的知识】

  • 对局部变量在赋值

♢ 变量只是单纯的初始值,可以在新函数中进行初始化。

♢ 变量如果有其他处理,必须要将它的值作为参数传给目标函数。

对局部变量在赋值

如果需要返回的变量不止一个,又该怎么办?

(1) 、提炼另一块代码,每个函数只返回一个值。要安排多个函数,用以返回多个值。

(2)、使用传递对象的方式进行

2、内联函数(Inline Method)

概要

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

【减少函数】

int getRating(){
    return (getLargeSize()) ? 2 : 1 ;
}
boolean getLargeSize(){
    return _largeSize > 5;
}
// 修改后
int getRating(){
    return (_largeSize > 5) ? 2 : 1 ;
}

动机

使用内部代码和函数名称同样清晰易懂,此时就应该去掉这个函数。间接性可能带来帮助,但是非必要的间接性总是让人不舒服。

太多的间接层,使得系统中所有的函数似乎只是对另一个函数的简单委托。使用内联手法,找出那些有用的间接层,同时去掉无用的间接层。

做法

♢ 检查函数,确定它不具有多态性(如果子类继承了这个函数,就不要将此函数内联,因为子类无法复写一个不存在的函数)【此问题目前IDE就可以帮助检查】

♢ 找出函数的所有的被调用点

♢ 将这个函数的所有被调用点替换为函数本体

♢ 编译、测试

♢删除该函数的定义

【内联函数 在真是的使用中可能不是那么容易,就如上面概要中的那个代码例子】,如果有十个函数使用getLargeSize(),然后将这个函数使用内联,那么带来一个问题,要修改判断条件 ,使用_largeSize > 10 ,那就要修改内联后所有函数,而不是只修改一个地方了。

使用的时候还是要仔细判断,谨慎选择。

3、内联临时变量(Inline Temp)

概要

你有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法。

将所有对该变量的引用动作,替换为对它赋值的那个表达式

double basePrice = order.basePrice();
return (basePrice > 100)

// 修改后
return (order.basePrice() > 100)

动机

唯一单独使用内联临时变量情况是:发现某个临时变量被赋予某个函数调用的返回值。 一般来说,如果临时变量不妨碍其他重构手法,留在那儿就行,妨碍了(如影响提炼函数)就应该将它内联化。

做法

♢ 检查该临时变量时候真的只被 赋值一次。

【我一般在代码中如果一个临时变量的赋值表达式多次使用,我会定义一个临时变量,其他地方使用这个临时变量】,例如:

void getPrice(){
    if(order.basePrice()){
        // do something
    }
    System.out.print("basePrice :" + order.basePrice());
    //这个函数 还有地方也使用 order.basePrice()
        
}
// 修改后
void getPrice(){
    double basePrice = order.basePrice();
    if(basePrice){
         // do something
    }
    System.out.print("basePrice :" + basePrice);
     //这个函数 还有地方也使用 basePrice
}

4、以查询取代临时变量(Replace Temp with Query)

概要

你的程序以一个临时变量保存某一次的运算结果。将这个表达式提炼到一个独立的函数中,将这个临时变量的所有的引用点替换为新函数的调用。

double basePrice = _quantity * _itemPrice;
if(basePrice > 100){
    return basePrice * 0.95;
}else{
    return basePrice * 0.98;
}

// 修改后
if(basePrice() > 100){
    return basePrice() * 0.95;
}else{
    return basePrice() * 0.98;
}
double basePrice(){
    return _quantity * _itemPrice;
}

可以思考一下这样的一个操作有什么好处?

【此重构手法和内联函数的重构手法进行对比,思考这些重构手法什么场景使用!】

动机

临时变量的问题在于:它们都是暂时的,而且只能在所属函数内部使用。 如果把临时变量替换为一个查询,那么同一个类都将可以访问获取这份信息。使得类编写更清晰的代码。

做法

♢ 找出只被赋值一次的临时变量。

♢ 将该临时变量申明为final (确保没有地方去修改这个临时变量)

上面例子的代码,不知道你是否仔细思考,这样的改动可以会带来性能问题,这里先不考虑它造成的性能的问题。

范例

查询取代临时变量

未完待续 …


如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到,谢谢!

如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!


欢迎访问我的csdn博客和关注的个人微信公众号!

愿你我在人生的路上能都变成最好的自己,能够成为一个独挡一面的人。

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页 : http://blog.csdn.net/u010648555

© 每天都在变得更好的阿飞

猜你喜欢

转载自blog.csdn.net/u010648555/article/details/87955081
今日推荐