在编程中,比较忌讳的一件事情就是长函数。因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚。因此,今天重构第一个手法就是处理长函数--Extract Method,抽取成一个独立的小函数。

我个人来说也很喜欢短小函数,因为他们代表了高强度的复用与灵活性。对于短小函数来说最最关键的就是短小函数的命名,其实你就是给了这些短小函数自我解释的机会,所以你如果给这些短小函数起一个接近其语义的名字,那当你读起长函数来说,就像是阅读一篇你设计好的故事。这个对于你之后的编程也是非常关键的。之前看到一篇文章说过,编程大牛与新手其中一个最重要的区别就是他们对于函数和类以及变量的命名非常谨慎。而我们往往一开始从学校走出来都是清一色的被老师教的i,j,k,尽快改掉这个习惯吧,现在的屏幕这么大,你也不需要去考虑纸张的问题,让变量,函数,类能够解释自身,省去你重新去理解的机会,这样岂不是更好?

一般来说,当你看到长函数,或者一堆函数实现挤在一起,意味着你需要对他们进行分离,又或者你需要实际下手为一堆函数写一个能够让人理解的注释的时候,表示你差不多也需要进行Extract Method了。其实很多时候,只有当你整理了这些函数之后,你才能看到高层应该能够看到的东西。比如这些代码段出现的位置,依赖的具体类更合适的是哪个,函数之间的逻辑关系有没有重复等等。因此,Extract Method能够帮助我更好的理解代码,能够站在更高的角度上去看待一些事情。『重构』一书中作者也提到,当他开始查看别人的代码或者接手别人的工作的时候,对于一些需要Extract Method的地方他会毫不犹豫去修改,重新命名。因为这个重构过程实质就是一个帮助你更好理解的过程。

Extract Method其实很简单,就是把在原来函数的内部的那些语句抽离出来,然后放到一个独立的目标小函数中去,然后在原来函数中的地方修改成对目标函数的调用就可以。但其中需要注意的地方就是局部变量这个东西。下面我们直接看第一个小例子

void printOwing()
{
// print banner
cout << "*********" << endl;
cout << "**Baner**" << endl;
cout << "*********" << endl;
}

这个例子是最简单的没有局部变量的例子,图中可以看到作者为了让你明白他接下来的3句是打印banner,特意加了注释。其实我们自己也可以看到这三句逻辑性其实就是应该放在一块,所以我们第一步就是创建目标函数printBanner,将这个代码片段复制进去。

void printBanner()
{
cout << "*********" << endl;
cout << "**Baner**" << endl;
cout << "*********" << endl;
}

接下来我们就是找到原来函数的引用点,将这个代码片段替换成对目标函数的调用。

void printOwing()
{
printBanner();
}

这样即完成了对这个函数的重构。下面来看第二个例子,注意,这个时候开始带局部变量了。(包括源函数的参数以及在源函数下声明的临时变量)

void printOwing()
{
QString bannerVersion = QString("1.0"); // print banner
cout << "*********" << endl;
cout << "**Baner**" << bannerVersion << endl;
cout << "*********" << endl;
}

注意这里的区别,此时已经用到了临时变量bannerVersion.并且这个变量是在源函数下声明的,仔细观察可以看到我的提炼代码段并不会去修改他们而是简单的去读取他们,因此我们可以把这种当作参数传给目标函数

void printOwing()
{
QString bannerVersion = QString("1.0"); printBanner(bannerVersion);
} void printBanner(const QString &value)
{
cout << "*********" << endl;
cout << "**Baner**" << value << endl;
cout << "*********" << endl;
}

这样就完成了对于这种情况的重构。停下来看看我们做了什么,有些同学可能觉得这不就是把原来的长函数变成短函数了吗?但你仔细观察你会看到,是的,复用来了,对于我提炼的printBanner这个函数,我只需要一个QString,我就可以完成打印banner的功能,并且灵活性也来了,我可以自由改变版本号version。

接下来我们看第三个例子

void printOwing()
{
Enumeration e = _orders.elements();
double outstanding = 0.0; printBanner(); // calcalate outstanding
while (e.hasMoreElements())
{
Order each = e.nextElement();
outstanding += each.getAmount();
}
}

在这个例子中,我们需要重构分离出计算outstanding的计算过程,但我们发现了临时变量outstanding需要在目标函数里进行赋值更改,所以我们必须通过目标函数来把它返回,又因为源函数的Enumeration e这个变量只在被提炼代码中会被用到,所以我们可以直接转移他进目标函数,并且发现outstanding这个变量其实就是一个初始化,因此我们最后提炼可以得到这样的结果

void printOwing()
{
printBanner();
double outStanding = getOutstanding();
} double getOutstanding()
{
Enumeration e = _orders.elements();
double outstanding = 0.0; // calcalate outstanding
while (e.hasMoreElements())
{
Order each = e.nextElement();
outstanding += each.getAmount();
} return outstanding;
}

另外还有一种情况就是这个变量既需要目标函数作为返回,又需要目标函数对其做处理,则需要将这个变量传给目标函数并将结果返回出来

void printOwing(int val)
{
Enumeration e = _orders.elements();
double outstanding = val * ; // calcalate outstanding
while (e.hasMoreElements())
{
Order each = e.nextElement();
outstanding += each.getAmount();
}
}

对于这种情况我们就要做到既要变量传入,也要将结果传出

void printOwing(int val)
{
double outstanding = val * ;
printBanner();
outStanding = getOutstanding(outStanding);
} double getOutstanding(int intialValue)
{
Enumerition e = _orders.elements();
double outstanding = intialValue; // calcalate outstanding
while (e.hasMoreElements())
{
Order each = e.nextElement();
outstanding += each.getAmount();
} return outstanding;
}

这样即完成了对于拥有临时变量的函数进行Extract Method,希望你会喜欢:)

『重构--改善既有代码的设计』读书笔记----Extract Method的更多相关文章

  1. 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object

    有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...

  2. 『重构--改善既有代码的设计』读书笔记----Move Method

    明确函数所在类的位置是很重要的.这样可以避免你的类与别的类有太多耦合.也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁.简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和 ...

  3. 『重构--改善既有代码的设计』读书笔记----Extract Class

    在面向对象中,对于类这个概念我们应该有一个清晰的责任认识,就是每个类应该只有一个变化点,每个类的变化应该只受到单一的因素,即每个类应该只有一个明确的责任.当然了,说时容易做时难,很多人可能都会和我一样 ...

  4. 『重构--改善既有代码的设计』读书笔记----Inline Method

    加入间接层确实是可以带来便利,但过多的间接层有时候会让我自己都觉得有点恐怖,有些时候,语句本身已经够清晰的同时就没必要再嵌一个函数来调用了,这样只会适得其反.比如 void test() { if ( ...

  5. 『重构--改善既有代码的设计』读书笔记----Introduce Foreign Method

    当你无法获得一个类的源代码或者没有权限去修改这个类的时候,你对于这种为你服务的类,你可能会出现需要别的需求的时候,比如一个Date类,你需要能够让他本身直接返回出他的后一天的对象,但他没有,这个时候你 ...

  6. 『重构--改善既有代码的设计』读书笔记----Replace Temp with Query

    Replace Temp with Query,顾名思义,表示你用查询来替换临时变量本身,临时变量对于函数来说是只有当前函数可见的,如果你在同类的别的地方要用到这个变量你就必须重新写表达式来获取这个变 ...

  7. 『重构--改善既有代码的设计』读书笔记----Substitute Algorithm

    重构可以把复杂的东西分解成一个个简单的小块.但有时候,你必须壮士断腕删掉整个算法,用简单的算法来取代,如果你发现做一件事情可以有更清晰的方式,那你完全有理由用更清晰的方式来解决问题.如果你开始使用程序 ...

  8. 『重构--改善既有代码的设计』读书笔记----Introduce Explaning Variable

    有时候你会遇到一系列复杂的表达式连续运算的时候,这个时候你可能根本招架不住如此长或者是如此复杂的长函数.这个时候你可以通过引用临时变量来储存他们的结果,将这些长函数的结果分成一个个临时变量来让函数清晰 ...

  9. 『重构--改善既有代码的设计』读书笔记----Change Value to Reference

    有时候你会认为某个对象应该是去全局唯一的,这就是引用(Reference)的概念.它代表当你在某个地点对他进行修改之后,那么所有共享他的对象都应该在再次访问他的时候得到相应的修改.而不会像值对象(Va ...

随机推荐

  1. The fundamental knowledge of Node JS.

    D3 JSJava scirpt is an awesome language for Internface Design.All Obejcts in JavaScirpt could be use ...

  2. WordPress BuddyPress Extended Friendship Request插件跨站脚本漏洞

    漏洞名称: WordPress BuddyPress Extended Friendship Request插件跨站脚本漏洞 CNNVD编号: CNNVD-201307-609 发布时间: 2013- ...

  3. BZOJ1679: [Usaco2005 Jan]Moo Volume 牛的呼声

    1679: [Usaco2005 Jan]Moo Volume 牛的呼声 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 723  Solved: 346[ ...

  4. 【动态规划】HDU 5492 Find a path (2015 ACM/ICPC Asia Regional Hefei Online)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5492 题目大意: 一个N*M的矩阵,一个人从(1,1)走到(N,M),每次只能向下或向右走.求(N+ ...

  5. 【模拟】Codeforces 710B Optimal Point on a Line

    题目链接: http://codeforces.com/problemset/problem/710/B 题目大意: 给N个点的坐标,在X轴上找到最靠左的点使得这个点到N个点距离之和最小. 题目思路: ...

  6. document.getElementById的简便方式

    封装自己的元素获取方法,使元素获取变得简便 注意:1.应该要防止定义的被重写,可将同名的重新定义   2.可将封装的对象置为全局对象,方便使用 通过id查找单个元素 封装方式: //通过id查找单个元 ...

  7. 2012蓝桥杯本科组C/C++决赛题

    星期几 [结果填空] (满分5分) 1949年的国庆节(10月1日)是星期六. 今年()的国庆节是星期一. 那么,从建国到现在,有几次国庆节正好是星期日呢? 只要答案,不限手段! 可以用windows ...

  8. VK Cup 2015 - Round 1 -E. Rooks and Rectangles 线段树最值+扫描线

    题意: n * m的棋盘, k个位置有"rook"(车),q次询问,问是否询问的方块内是否每一行都有一个车或者每一列都有一个车? 满足一个即可 先考虑第一种情况, 第二种类似,sw ...

  9. 平衡二叉树(AVL)java实现

    数的节点 package com.ydp.tree.AVLTree; public class Node{ private int data = 0; private Node lchild = nu ...

  10. WdatePicker 控制选择范围

    1. 跨无限级框架显示 无论你把日期控件放在哪里,你都不需要担心会被外层的iframe所遮挡进而影响客户体验,因为My97日期控件是可以跨无限级框架显示的 示例2-7 跨无限级框架演示 可无限跨越框架 ...