浏览 2529 次
锁定老帖子 主题:大话重构连载17:抽取方法的实践
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2014-10-29
最后修改:2014-10-29
前面的工作为抽取函数做好了准备,但你不必阅读和整理完所有的代码才开始抽取。许多遗留函数的大函数非常长,你可以整理一部分,就开始着手重构。比如,把刚才分段的代码段抽取出来,形成一个独立的函数。在这里,代码段与注释,是我们决定是否需要抽取成函数的重要标志之一。 在阅读过程中,许多长相相似的代码也是我们需要重视的重要代码。重复代码是许多遗留系统代码质量差的重要原因之一,因此提高代码复用就成为了代码优化一个重要的项目。整合大量重复的代码,将其提取到一个统一的函数中为其它各处所调用,是一个值得推荐的办法。因此,重复代码也是抽取函数的重要标志。 除此之外,一些块操作的语句,如条件语句、循环语句、try语句,都可能成为抽取函数的标志。最典型的就是if语句,包含在if语句中间的常常是一个相对独立的功能,譬如一次重构中,原代码长得像这样: ...... if (cmd != null && cmd.equals("chkCard")){ //此处省略了500行 } else if (cmd != null && cmd.equals("chkIc")){ //此处省略了300行 } else if (cmd != null && cmd.equals("chkBuffer")){ //此处省略了1000行 } ...... 整个这段代码有数千行之多,但整体结果就是用这样的一系列if语句组成。随后,我将每个if语句中的代码都提取出来形成了各自的函数。它们被重构成这样: ...... if (cmd != null && cmd.equals("chkCard")){ byte[] ret = chkCard(reader); servletOutput(res, ret); } else if (cmd != null && cmd.equals("chkIc")){ byte[] ret = chkIc(reader); servletOutput(res, ret); } else if (cmd != null && cmd.equals("chkBuffer")){ byte[] ret = chkBuffer(reader); servletOutput(res, ret); } ...... 这样,原来if语句中的业务操作代码,就被抽取到chkCard(), chkIc(), chkBuffer()这样的函数中了。起初我们将if语句中的所有代码都抽取出来写入这些函数中,这是十分自然而然想到的办法。但随后发现这样需要将response作为参数传递给这些函数中,这样的设计不太好。因此,将代码还原回来重新重构,将写入response的操作写入到servletOutput()中了。整个过程如图5.1所示: 图5.1分解大函数的示例
完成了此次重构以后,我们原来这个超级大函数由数千行代码,缩减到了百来行代码,这是一个可喜的进步,函数变得结构清晰而易于阅读。但是,被抽取出来的新函数却依然庞大,它们有的会达到一千多行,阅读依然困难。这时我们运用“抽取方法”继续分解。比如这个chkCard(),它执行的是一大堆校验,每个校验其功能都相对独立。因此,我首先调整代码顺序,将每个校验的代码都独立成一段,在前面添加相应的注释。然后使用抽取方法,将校验抽取到一个一个函数中。 整个数千行代码的超级大函数,就这样原子裂变式地逐渐分解,最后分解成数十个函数。每个函数只有数十行代码,并通过注释标注它们的用途与参数、返回值含义。这样,一个起初难于阅读的函数,经过一系列重构,开始变得可以阅读了。是的,我们开始迈出了可喜地一步。但一个对象包含了数十个方法,这些方法被凌乱地堆砌在一起,没有层级、没有主次。最关键是,虽然每个方法都不大,但这个对象却包含数千行代码,依然显得臃肿。因此我们还需要后面的步骤继续重构。 大话重构连载首页:http://fangang.iteye.com/blog/2081995 特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |