本篇文章的内容来自《重构 改善既有代码的设计》一书学习笔记整理并且加上自己的浅显的思考总结!
重构手法中,很大一部分是对函数进行整理,使之更恰当地包装代码。
重新组织函数
对过长的函数进行拆解,提炼函数,并处理局部变量,使得拆解后的函数更加清晰并且能够更好的工作。
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
© 每天都在变得更好的阿飞