使用抽取方法,虽然道理十分简单,但实际操作起来却并不是那么容易的。完成抽取方法最大的困难,就是如何处理抽取函数与原函数的数据交换。如同将一颗大树从土壤里拔出来,盘根错节的根茎,那是剪不断理还乱。当代码还没有被抽取出来之前,它们与其它程序都是在一个函数的内部,因此各个代码段可以毫无顾忌地相互交互数据。但当我们将代码从原函数中抽取出来时,抽取出来的代码与原函数中的代码就形成了一道墙,要交换的数据只能通过参数与返回值进行交互,这将给我们带来诸多麻烦。
将代码从原函数中抽取出来,放进新的函数中,首先就会显示许多的错误,即显示许多变量未定义,这就为我们分析有哪些变量需要交互提供了线索。将所有要交互的数据都写到函数的参数中,理论上是没有问题的,却不是一个好的设计。一方面,它显得非常丑陋,长长的参数列表,使程序变得很傻而难于理解;另一方面,它也留下了一个隐患:当程序发生变更时,很可能因为增加参数而改变函数的接口,这是我们不愿看到而尽量避免的。因此,在处理数据交互方面,我更愿意选择使用值对象。值对象(Value Object)就是没有任何业务,仅仅用于传递数据的简单对象。将一堆变量杂乱无章地塞进一个值对象中,不是不可以。实际上,在重构的初期,我们往往就是这样做的。但是,随着重构的深入,值对象也在逐渐优化,最终每个值对象都应当对应现实业务中的一个事物,而它所包含的变量,就是这个事物应当拥有的属性。因此,最终函数传递的参数,应当是几个值对象,以及这些值对象以外的几个变量。建议一个函数的参数不要超过六个,最好是1~4个。
另外一个比较麻烦的问题就是返回值。在Java世界,函数的返回值只能是一个。如果我们的程序需要返回2个,甚至更多时,怎么办呢?返回一个值对象,是个好的办法。但在一个函数中,传递的参数是个值对象,返回值又是另一个值对象,如果都这样设计就会出现大量的值对象,造成值对象的泛滥。将要返回的数据直接塞进参数值对象中,是我们可以采用的另一个好的方法。在Java语言中没有形参实参的区别,如果传递进去的是一个对象,在函数中将该对象的某个属性进行了修改,即使程序已经跳出了抽取函数而回到原函数,该修改也依然起效。通过原函数对该对象的访问,就实质性地实现了抽取函数返回值的功能。从另外一个角度来说,我们的函数,就是对这些值对象中数据的处理过程。这个过程就像一个摩托车生产线,就这辆摩托,你先装上一个发动机,我再安两个轮子。我们的处理过程被设计成由一系列函数,依次对某个值对象进行连续处理。这样值对象就变成一个载体,在原函数与抽取函数之间交互数据。这样,前面讨论的参数与返回值的问题将得到解决。
比如,我们的系统现在从前端提交了一个表单,我们首先通过Servlet或Action获得前端传递过来的数据,填入一个值对象中。然后该表单可能需要一系列校验,因此我们将这个值对象传递给每个校验函数中。校验函数对值对象进行一些校验,可能还会对值对象写入一些标志。接着可能会调用一些处理函数,从数据库中查询一些数据填入到该值对象中。经过一系列的校验与处理,值对象中的数据被填补完整了,最后交给持久化函数去写库。所有这些函数的调用都只需要传递这个值对象就可以了,甚至可以没有返回值,问题得到解决。
除此之外,实践抽取方法中另一个总是让我们反复思考的问题,就是那个“画红线”的问题。正如前面所说,原函数中的那些块操作,如条件语句、循环语句、try语句,都是进行抽取操作比较明显的标志。但关键问题是,这并不意味着那些块操作中所有的代码都是抽取函数的范围,我们需要考虑许多的因素。当然,最初最直观地想法是将块操作中所有的代码抽取出来。但是,在放入函数,在分析它的参数列表与返回值时,我们可能会发现问题。一些参数,如request、response等,我们并不希望作为参数直接传递进来。这时候,将抽取函数中最前面或最后面的部分代码移出该函数,放回原函数,是一个比较直观的想法。还有,当一些函数功能相似、位置并列时,后续我们可能会对它们进行相应的归并处理,因此在此时抽取函数时,可能期望能统一画线在相同的位置上。以上这些问题都是我们在重构过程中需要仔细考虑的问题。
最后我们要讨论的话题是抽取方法的命名。过去我们对方法的命名常常有些令人摸不着头脑,究其原因,一方面是程序员对这方面过于随意,而另一方面是我们所站的角度不对。我们应当站在使用者的角度命名,而不是开发者的角度。我们应该告诉使用者,调用这个方法会执行什么功能,即它的操作意图是什么。这样使用者才能正确理解,并在合理的地方使用。前例中那个getBlsj()就是一个最好的明证(详见 5.1 超级大函数):起初命名为getBlsj()就是因为站在开发人员的角度它就是获取办理时间的,如果这样命名则其他使用者只能用它来获取办理时间。然而它真实的意图是什么呢?是转换日期格式,因此将其改名为transformDate()。正因为这样的改名,其他开发者才明白可以用它转换其它类似的日期,而不仅仅是办理时间。这足见方法的命名是如此重要,我们不得不察。
此外,我给大家的建议是命名不要过于专业。对于一些专业术语,查字典去找一个连自己都不认识的英语单词,你自己都不认识,怎么能够要求你的读者认识呢!命名的目的是为了增加可读性,因此专业的英语单词无助于提高可读性,应当尽量避免,而一些约定俗成的拼音未尝不可。比如纳税人识别号,有人命名为taxpayerId或者taxpayerCode。但如果整个行业都使用nsrsbh,这样写大家都看得懂,反倒taxpayerId或者taxpayerCode显得比较突兀。再比如发票,有人查了字典,命名为invoice。Invoice可以指发票,也可以指其它各种与商品清单有关的单据,因此不准确,还不如命名为fp简单明了。
大话重构连载首页:
http://fangang.iteye.com/blog/2081995
特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!
分享到:
相关推荐
Android之大话设计模式——:抽象工厂模式借鉴.pdf
大话Oracle RAC:集群、高可用性、备份与恢复(带目录清晰中文完整版)
大话Oracle RAC:集群、高可用性、备份与恢复。 此书被认为不可多得的好资料之一:大话Oracle RAC(PDF经典),看完之后深有感触,发出来共享一下。
7. **故障诊断与问题解决**:分享RAC故障排查的常见方法,包括日志分析、使用CRS(Cluster Ready Services)诊断工具,以及如何快速响应和解决问题。 8. **案例研究**:通过实际案例来展示RAC在不同场景下的应用,...
Android之大话设计模式——:抽象工厂模式参考.pdf
综上所述,《大话存储:存储系统底层架构原理极限剖析(终极版)》是一本全面覆盖存储系统理论与实践的书籍,对于IT从业者,无论是想提升基础理论还是解决实际问题,都能从中获得宝贵的启示和知识。通过学习本书,...
《大话移动APP测试》是一本详尽介绍Android与iOS平台测试应用的指南,旨在帮助读者深入理解并掌握移动应用的测试技术。本书全面覆盖了移动端的测试领域,包括平台特性、设备兼容性、功能测试、性能测试、安全测试等...
《大话Oracle RAC:集群 高可用性 备份与恢复》按照“发现问题→解决问题→实践与理论相结合”的方式进行介绍,首先对现实问题进行分析,然后提供合适的解决方案,最后自然地引出Oracle中的理论知识点,这种讲解...
初中语文文摘历史“大话王”郭台铭:被夏普狠狠摔了个大跟
大话存储:存储系统底层架构原理极限剖析(终极版)第4部分 大话存储:存储系统底层架构原理极限剖析(终极版)第4部分
总的来说,通过《大话数据分析:Tableau数据可视化实战》的数据集,学习者可以深入实践如何加载数据、构建视图、创建交互式仪表板,并用Tableau来解答业务问题。这涵盖了从数据导入、数据探索、可视化设计到故事呈现...
大话存储2:存储系统架构与底层原理极限剖析》内容简介:网络存储是一个涉及计算机硬件以及网络协议/技术、操作系统以及专业软件等各方面综合知识的领域。目前国内阐述网络存储的书籍少之又少,大部分是国外作品,对...
《大话Java:从零基础到数据库、Web开发》并不单单从基础知识的角度来讲解Java,而是从解决问题的角度来介绍Java语言。书中介绍的大量实用的开发案例,既能让读者巩固每章的知识,又可以让读者学以致用,激发编程...
大话存储:存储系统底层架构原理极限剖析(终极版)第3部分 大话存储:存储系统底层架构原理极限剖析(终极版)第3部分大话存储:存储系统底层架构原理极限剖析(终极版)第3部分
大话存储:存储系统底层架构原理极限剖析(终极版)_张冬2015.01_P989
《大话3WDF解包器:深入解析与应用》 在计算机游戏中,资源的管理与存储是一项重要的技术,尤其对于大型多人在线游戏(MMORPG)如“大话西游”系列来说,如何高效地组织和加载游戏资源是至关重要的。本文将详细探讨...
"《大话统计学》R 程序包-函数" 《大话统计学》R 程序包-函数是一本关于统计学和 R 语言的教材,涵盖了统计学的多个方面,包括描述统计、概率理论、随机变量、概率分布、抽样理论、区间估计、假设检验等。该书章节...
大话移动APP测试 Android与iOS应用测试指南