重构-改善既有代码的设计:重新组织函数的九种方法(四)
来源:互联网 发布:电视盒子root软件 编辑:程序博客网 时间:2024/05/18 01:19
函数过长或者逻辑太混乱,重新组织和整理函数的代码,使之更合理进行封装。
1. Extract Method 提炼函数
提炼函数:(由复杂的函数提炼出独立的函数或者说大函数分解成由小函数组成)你有一段代码可以被组织在一起并独立出来。将这段代码放进一个独立函数,并让函数名称解释该函数的用途。- void printOwing() {
- //print banner
- System.out.println(“*********”);
- System.out.println(“Banner”);
- System.out.println(“*********”);
- //print details
- System.out.println ("name: " + _name);
- System.out.println ("amount " + getOutstanding());
- }
- void printOwing() {
- printBanner();
- printDetails(getOutstanding());
- }
- Void printBanner() {
- //print banner
- System.out.println(“*********”);
- System.out.println(“Banner”);
- System.out.println(“*********”);
- }
- <strong>void printDetails (double outstanding) {
- System.out.println ("name: " + _name);
- System.out.println ("amount " + outstanding);
- } </strong>
一个函数多长才算合适?长度不是问题,关键在于函数名称和函数本体之间的语义距离。如果提炼可以强化代码的清晰度,那就去做,就算函数名称必提炼出来的代码还长也无所谓。
2. Inline Method 内联函数
- int getRating() {
- return moreThanfiveLateDeliverise() ? 2 : 1;
- }
- bool moreThanfiveLateDeliverise() {
- return _numberOfLateLiveries > 5;
- }
- int getRating(){
- return _numberOfLateLiveries > 5 ? 2 : 1;
- }
另一种需要使用Inline Method (内联函数)的情况是:你手上有一群不甚合理的函数。你可以将它们都内联到一个大型函数中,再从中提炼出合理的小函数。实施Replace Method with Method Object (以函数对象取代函数)之前这么做,往往可以获得不错的效果。你可以把所要的函数的所有调用对象的函数内容都内联到函数对象中。比起既要移动一个函数,又要移动它所调用的其他所有函数,将整个大型函数作为整体来移动比较简单。
如果别人使用了太多间接层,使得系统中所有函数都似乎只是对另一个函数的简单委托,造成在这些委托动作之间晕头转向,那么就使用 Inline Method (内联函数)。
当然,间接层有其价值,但不是所有间接层都有价值。试着使用内联手法,可以找出那些有用的间接层,同时将那些无用的间接层去除。
3.Inline Temp 内联临时变量
- double basePrice = anOrder.BasePrice();
- return basePrice(>1000);
- return (anOrder.BasePrice() >1000);
Inline Temp(内联临时变量)多半是作为Replace Temp with Query(以查询取代临时变量)的一部分使用的,所以真正的动机出现在后者那里。唯一单独使用Inline Temp(内联临时变量)情况是:你发现某个临时变量被赋予某个函数调用的返回值。
一般来说,这样的临时变量不会有任何危害,可以放心把它留在那。但如果这个临时变量妨碍了其他的重构手法,例如 Extract Method (提炼函数),就应该将它内联化。
4.Replace Temp with Query 以查询代替临时变量
以查询代替临时变量:(独立函数代替表达式)你的程序以一个临时变量保存某一个表达式的运算效果。将这个表达式提炼到一个独立函数中。将这个临时变量的所有引用点替换为对新函数的调用。此后,新函数就可以被其他函数调用。
- double basePrice = _quantity*_itemPrice;
- if (basePrice > 1000) {
- return basePrice * 0.95;
- else
- return basePrice * 0.98;
- if (basePrice() > 1000)
- return basePrice() * 0.95;
- else
- return basePrice() * 0.98;
- ……
- double basePrice() {
- return _quantity * _itemPrice;
- }
以查询代替临时变量往往是你运用Extract Method(提炼函数)之前必不可少的一个步骤。局部变量会使代码难以被提炼,所以你应该尽可能把它们替换为查询式。
这个重构手法较为简单的情况是:临时变量只被赋值一次,或者赋值给临时变量的表达式不受其他条件影响。其他情况比较棘手,但也可能发生。你可能需要先运用Split Temporary Variable(分解临时变量)或Separate Query form Modifier(将查询函数和修改函数分离) 使情况变得简单一些,然后再替换临时变量。如果你想替换的临时变量是用来收集结果的)例如循环中的累加值),就需要将某些程序逻辑(例如循环)复制到查询函数去。
5.Introduce Explaining Variable 引入解释性变量
- if (Platform.ToUpperCass().indexOf("MAC") > -1 && (Browser.ToUpperCass().indexOf("Ie") > -1) && WasInitalized() ) {
- //do something
- }
- const bool imMacOs = Platform.ToUpperCass().indexOf("MAC") > -1;
- const bool isIeBrowser = Browser.ToUpperCass().indexOf("Ie") > -1;
- const bool wasInitalized = WasInitalized();
- if (imMacOs && isIeBrowser && wasInitalized)
- {
- //do something
- }
在条件逻辑中,Introduce Explaining Variable(引入解释性变量)是一个很常见的手法,但是最好尽量使用 Extract Method(提炼函数) 来解释一段代码的意义。毕竟临时变量只在他所处的那个函数才有意义,局限性较大。函数则可以在对象的这个生命中都有用,并且可被其他对象使用。但有时候,当局部变量使Extract Method(提炼函数)难以进行时,就可以使用Introduce Explaining Variable (引入解释性变量).
6. Split Temporary Variable 分解临时变量
分解临时变量:(临时变量不应该被赋值超过一次)你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果。针对每次赋值,创造一个独立、对应的临时变量
- double temp = 2 + (_height + _width);
- Console.WriteLine(temp);
- temp = _height * _width;
- Console.WriteLine(temp);
- const double perimeter = 2 + (_height + _width);
- Console.WriteLine(perimeter);
- const double area = _height * _width;
- Console.WriteLine(area);
结果收集变量负责将“通过这个函数的运算”而构成的某个值收集起来。
除了这2种情况,还有很多临时变量保存一段冗长代码的运算结果,以便稍后使用。这种临时变量应该只被赋值一次。如果它们被赋值超过一次,
就意味着它们在函数中承担了一个以上的职责。如果临时变量承担多个责任,它就应该被替换为多个临时变量,每个变量只承担一个责任。同一个临时变量承担2件不同的事情,会令代码阅读者糊涂。
7.Remove Assigments to Parameters 移除对参数的赋值
- 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;
- }
如果参数是Object,容易误赋值。采用final来防止误用参数:
要清楚“对参数赋值”这个说法的意思。如果你把一个名为foo的对象作为参数传给某个函数,那么“对参数赋值”意味着改变foo,使它引用另外一个对象。如果你在“被传入对象”身上进行什么操作,那没什么问题。这里只针对“foo被改而指向另一个对象”这种情况来讨论。
- <span style="font-size:12px;">void aMethod(Object foo) {
- foo.modifyInSomeWay();
- foo = anotherObject;
- }</span>
这样的做法降低了代码的清晰度,而且混用了按值传递和按引用传递这2种参数传递方式。
在按值传递的情况下,对参数的任何修改,都不会对调用端造成任何影响。那些用过按引用传递方式的人可能会在这一点上犯糊涂。
另一个让人糊涂的地方时函数本体内。如果你只以参数表示“被传递进来的东西”。那么代码会清晰地多,因为这种用法在所有语言都表现出相同语义。
8. Replace Method with Method object 函数对象取代函数
- class Order...
- double price() {
- double primaryBasePrice;
- double secondaryBasePrice;
- double tertiaryBasePrice;
- // long computation; ...
- }
或者可以采用static method
局部变量的存在会增加函数分解的难度。如果一个函数之中局部变量泛滥,那么想分解这个函数是非常困难的。Replace Temp with Query (以查询取代临时变量)可以帮助你减轻这一负担,但有时候你会发现根本无法拆解一个需要拆解的函数。这种情况下,应该使用函数对象。9.Substitute Algorithm 替换算法
替换算法:(函数本体替换为另一个算法)你想要把某个算法替换为另一个更清晰地算法。将函数本体替换为另一个算法。- String foundPerson(String[] people){
- for (int i = 0; i < people.length; i++) {
- if (people[i].equals ("Don")){
- return "Don";
- }
- if (people[i].equals ("John")) {
- return "John";
- }
- if (people[i].equals ("Kent")){
- return "Kent";
- }
- }
- return "";
- }
- String foundPerson(String[] people){
- List candidates
- = Arrays.asList(new String[] {"Don", "John", "Kent"});
- for (int i=0; i<people.length; i++)
- if (candidates.contains(people[i]))
- return people[i]; return "";
- }
有时候你会想要修改原先的算法,让它去做一件与原先略有差异的事。这时候你也可以先把原先的算法替换为一个较易修改的算法,这样后续的修改会轻松许多。使用这项重构之前,请先确定自己尽可能分解了原先函数。替换一个巨大而复杂的算法是很困难的。只有先将它分解为较简单的小型函数,然后你才能很有把握的进行算法替换工作。
- 重构-改善既有代码的设计:重新组织函数的九种方法(四)
- 重构-改善既有代码的设计:重新组织函数的九种方法(四)
- 重构改善既有代码的设计--重新组织函数
- 重构-改善既有代码的设计:重新组织数据的16种方法(六)
- 重构-改善既有代码的设计:重新组织数据的16种方法(六)
- 《重构改善既有代码的设计》之重构列表--重新组织函数(一)
- 《重构改善既有代码的设计》之重构列表--重新组织函数(二)
- 《重构改善既有代码的设计》之重构列表--重新组织函数(三)
- 重构改善既有代码的设计--重新组织数据
- 《重构改善既有代码的设计》之重构列表--重新组织数据(四)
- PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
- PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
- PHP:《重构-改善既有代码的设计》之一 重新组织你的函数
- 重构改善既有代码设计-----重新组织函数
- 《重构--改善既有代码的设计》--重新组织函数(6)
- 《重构--改善既有代码的设计》总结三之重新组织函数
- 2016书单总结--重构改善既有代码的设计--重新组织函数
- 重构 改善既有代码的设计—— 重新组织方法
- 如何使用 DataInputStream and DataOutputStream?
- 重构-改善既有代码的设计:对象之间移动特性的八种方法(五)
- [1]工欲善其事必先利其器-------UML的使用(一)
- win8.1休眠状态下不能进入系统
- Android单线程模型说明
- 重构-改善既有代码的设计:重新组织函数的九种方法(四)
- Java.math.BigDecimal.movePointLeft()方法实例
- 字符串和多维数组实验(实验四) 实验报告
- ZigBee学习之10——MAC层API解读2
- 重构-改善既有代码的设计:重构原则(二)
- 重构-改善既有代码的设计:重新组织数据的16种方法(六)
- 使用pthread后,界面假死现象问题
- 谁的青春不迷茫
- 重构-改善既有代码的设计:编写代码22宗罪(三)