写在前面

\\

现在的软件系统开发难度主要在于其复杂度和规模,客户需求也不再像Winston Royce瀑布模型期望那样在系统编码前完成所有的设计满足用户软件需求。在这个信息爆炸技术日新月异的时代,需求总是在不断的变化,随之在2001年业界17位大牛聚集在美国犹他州的滑雪胜地雪鸟(Snowbird)雪场,提出了“Agile”(敏捷)软件开发价值观,并在他们的努力推动下,开始在业界流行起来。在《代码整洁之道》一书中提出:一种软件质量,可持续开发不仅在于项目架构设计,还与代码质量密切相关,代码的整洁度和质量成正比,一份整洁的代码在质量上是可靠的,为团队开发,后期维护,重构奠定了良好的基础。

\\

接下来笔者将结合自己之前的重构实践经验,来探讨平时实际开发过程中我们注重代码优化实践细节之道,而不是站在纯空洞的理论来谈论代码整洁之道。

\\

在具体探讨如何进行代码优化之前,我们首先需要去探讨和明确下何谓是“代码的坏味道”,何谓是“整洁优秀代码”。因为一切优化的根源都是来自于我们平时开发过程中而且是开发人员自己产生的“代码坏味道”。

\\

代码的坏味道

\\

“如果尿布臭了,就换掉它。”-语出Beck奶奶,论抚养小孩的哲学。同样,代码如果有坏味道了,那么我们就需要去重构它使其成为优秀的整洁代码。

\\

谈论到何谓代码的坏味道,重复代码(Duplicated Code)首当其冲。重复在软件系统是万恶的,我们熟悉的分离关注点,面向对象设计原则等都是为了减少重复提高重用,Don’t repeat yourself(DRY)。关于DRY原则,我们在平时开发过程中必须要严格遵守。

\\

其次还有其他坏味道:过长函数(Long Method)、过大的类(Large Class)、过长参数列表(Long Parameter List)、冗余类(Lazy Class)、冗余函数(Lazy Function)无用函数参数(Unused Function Parameter)、函数圈复杂度超过10(The Complexity is over 10)、依恋情结(Feature Envy)、Switch过多使用(Switch Abuse)、过度扩展设计(Over-extend design)、不可读或者可读性差的变量名和函数名(unread variable or function name)、异曲同工类(Alternative Classes with Different Interfaces)、过度耦合的消息链(Message Chains)、令人迷惑的临时字段(Temporary Field)、过多注释(Too Many Comments)等坏味道。

\\

整洁代码

\\

什么是整洁代码?不同的人会站在不同的角度阐述不同的说法。而我最喜欢的是Grady Booch(《面向对象分析与设计》作者)阐述:

\\

“整洁的代码简单直接。整洁的代码如同优美的散文。整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。”

\\

整洁的代码就是一种简约(简单而不过于太简单)的设计,阅读代码的人能很清晰的明白这里在干什么,而不是隐涩难懂,整洁的代码读起来让人感觉到就像阅读散文-艺术的沉淀,作者是精心在意缔造出来。

\\

整洁代码是相对于代码坏味道的,如何将坏味道代码优化成整洁代码,正是笔者本文所探讨的重点内容:整洁代码之道-重构,接下来笔者将从几个角度重点描述如何对软件进行有效有技巧的重构。

\\

重构 — Why

\\

在软件开发过程中往往开发者不经意间就能产生代码的坏味道,特别是团队人员水平参差不齐每个人的经验和技术能力不同的情况下更容易产生不同阶段的代码坏味道。并且随着需求的迭代和时间推移,代码的坏味道越来越严重,甚至影响到团队的开发效率,那么遇到这个问题该如何去解决。

\\

在软件开发Coding之前我们不可能事先了解所有的需求,软件设计肯定会有考虑不周到不全面的地方,而且随着项目需求的Change,很有可能原来的代码设计结构已经不能满足当前需求。

\\

更何况,我们很少有机会从头到尾参与并且最终完成一个项目,基本上都是接手别人的代码,即使这个项目是从头参与的,也有可能接手团队其他成员的代码。我们都有过这样的类似的抱怨经历,看到别人的代码时感觉就像垃圾一样特别差劲,有一种强烈的完全想重写的冲动,但一定要压制住这种冲动,你完全重写,可能比原来的好一点,但浪费时间不说,还有可能引入原来不存在的Bug,而且,你不一定比原来设计得好,也许原来的设计考虑到了一些你没考虑到的分支或者异常情况。

\\

我们写的代码,终有一天也会被别人接手,很可能到时别人会有和我们现在一样的冲动,所以开发者在看别人代码时候,要怀着一颗学习和敬畏之心,去发现别人的代码之美,在这个过程中挑出写的比较好的优秀代码,吸取精华,去其糟粕,在这个基础上,我们再去谈重构,那么你的重构会是一个好的开端。

\\

总之,我们要做的是重构不是重写,要先从小范围的局部重构开始,然后逐步扩展到整个模块。

\\

重构 — 作用

\\

重构,绝对是软件开发写程序过程中最重要的事之一。那么什么是重构,如何解释重构。名词:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。动词:使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

\\

重构不只可以改善既有的设计结构,还可以帮助我们理解原来很难理解的流程。比如一个复杂的条件表达式,我们可能需要很久才能看明白这个表达式的作用,还可能看了好久终于看明白了,过了没多长时间又忘了,现在还要从头看,如果我们把这个表达式运用Extract Method抽象出来,并起一个易于理解的名字,如果函数名字起得好,下次当我们再看到这段代码时,不用看逻辑我们就知道这个函数是做什么的。

\\

如果对这个函数内所有难于理解的地方我们做了适当的重构,把每个细小的逻辑抽象成一个小函数并起一个容易理解的名字,当我们看代码时就有可能像看注释一样,不用再像以前一样通过看代码的实现来猜测这段代码到底是做什么的,我一直坚持和秉持这个观点:好的代码胜过注释,毕竟注释还是有可能更新不及时的,不及时最新的注释容易更其他人带来更多的理解上的困惑。

\\

此外重构可以使我们增加对代码和业务逻辑功能的理解,从而帮助我们找到Bug;重构可以帮助我们提高编程速度,即重构改善了程序结构设计,并且因为重构的可扩展性使添加新功能变得更快更容易。

\\

重构 — 时机

\\

理解了重构的意义和作用,那么我们何时开始重构呢?笔者一直坚持这种观点:重构是一个持续的系统性的工程,它是贯穿于整个软件开发过程中,我们无需专门的挑出时间进行重构,重构应该随时随地的进行,即遵循三次法则:事不过三,三则重构。这个准则表达的意思是:第一次去实现一个功能尽管去做,但是第二次做类似的功能设计时会产生反感,但是还是会去做,第三次还是实现类似的功能做同样的事情,那你就应该去重构。三次准则比较抽象,那么对应到我们具体的软件开发流程中,一般可以在这三个时机去进行:

\\

(1) 当添加新功能时如果不是特别容易,可以通过重构使添加特性和新功能变得更容易。在添加新功能的时候,我们就先清理这个功能所需要的代码。花一点时间,用滴水穿石的方法逐渐清理代码,随着时间的推移,我们的代码就会越来越干净,开发速度也会越来越快。

\\

(2) 修改Bug的时候去重构,比如你在查找定位Bug的过程中,发现以前自己的代码或者别人的代码因为设计缺陷比如可扩展性、健壮性比较差造成的,那么此时就是一个比较好的重构时机。可能这个时候很多同学就有疑问了,认为我开发要赶进度,没有时间去重构,或者认为我打过补丁把Bug解决不就行了,不需要去重构。根据笔者之前多年的经验得出的结论:遇到即要解决即那就是每遇到一个问题,就马上解决它,而不是选择绕过它。完善当前正在使用的代码,那些还没有遇到的问题,就先不要理它。在当前前进的道路上,清除所有障碍,以后你肯定还会再一次走这条路,下次来到这里的时候你会发现路上不再有障碍。

\\

软件开发就是这样。或许解决这个问题需要你多花一点时间。但是从长远来看,它会帮你节省下更多的时间。也就是重构是个循序渐进的过程,经过一段时间之后,你会发现之前所有的技术债务会逐步都不见了,所有的坑相继都被填平了。这种循序渐进的代码重构的好处开始显现,编程的速度明显会加快。

\\

(3)Code Review时去重构,很多公司研发团队都会有定期的Code Review,这种活动的好处多多,比如有助于在开发团队中传播知识进行技术分享,有助于让较有经验的开发者把知识传递给欠缺经验的人,并帮助更多的人对软件的其他业务模块更加熟悉从而实现跨模块的迭代开发。Code Review可以让更多的人有机会对自己提出更多优秀好的建议。同时重构可以帮助审查别人的代码,因为在重构前,你需要先阅读代码得到一定程度的理解和熟悉,从而提出一些建议和好的idea,并考虑是否可以通过重构快速实现自己的好想法,最终通过重构实践你会得到更多的成就感满足感。为了使审查代码的工作变得高效有作用,据我以前的经验,我建议一个审查者和一个原作者进行合作,审查者提出修改建议,然后两人共同判断这些修改是否能够通过重构轻松实现,如果修改成本比较低,就在Review的过程中一起着手修改。

\\

如果是比较大型比较复杂的设计复查审核工作,建议原作者使用UML类序列图、时间序列图、流程图去向审查者展现设计的具体实现细节,在整个Code Review中,审查者可以提出自己的建议或者修改意见。在这种情景下,审查者一般由团队里面比较资深的工程师、架构师、技术专家等成员组成。

\\

关于Code Review的形式,还可以采取极限编程中的“结对编程”形式。这种形式可以采取两个人位置坐在一起去审查代码,可以采取两个平台比如IOS 和android 的开发人员一起去审查,或者经验资深的和经验不资深的人员一起搭配去审查。

\\

重构的这三个时机要把握好原则,即什么时候不应该重构,比如有时候既有代码实现太混乱啦,重构它还不如重新写一个来得简;此外,如果你的项目已经进入了尾期,此时也应该避免重构,这时机应该尽可能以保持软件的稳定性为主。

\\

理解了重构是做什么,重构的作用,为什么要重构,以及重构的时机,我们对重构有了初步认识,接下来笔者重点篇幅来讲解如何使用重构技巧去优化代码质量达成Clean Code .

\\

重构技巧 — 函数重构

\\

重构的源头一切从重构函数开始,掌握函数重构技巧是重构过程中很关键的一步,接下来我们来探讨下函数重构有那些实用技巧。

\\

  • \\t

    重命名函数(Rename Function Name) : Clean Code要求定义的变量和函数名可读性要强,从名字就可以知道这个变量和函数去做什么事情,所以好的可读性强的函数名称很重要,特别是有助于理解比较复杂的业务逻辑。

    \\t\\t

  • \\t

    移除参数(Remove Parameter): 当函数不再需要某个参数时,要果断移除,不要为了某个未知需求预留参数,过多的参数会给使用者带来参数困扰。

    \\t\\t

  • \\t

    将查询函数和修改函数分离:如果某个函数既返回对象值,又修改对象状态。这时候应该建立两个不同的函数,其中一个负责查询,另一个负责修改。如果查询函数只是简单的返回一个值而没有副作用,就可以无限次的调用查询函数。对于复杂的计算也可以缓存结果。

    \\t\\t

  • \\t

    令函数携带参数:如果若干函数做了类似的工作,只是少数几个值不同导致行为略有不同,合并这些函数,以参数来表达不同的值。

    \\t\\t

  • \\t

    以明确函数取代参数:有一个函数其中的逻辑完全取决于参数值而采取不同行为,针对该参数的每一个可能值建立一个单独的函数。

    \\t\\t

  • \\t

    保持对象完整性:如果你需要从某个对象取若干值,作为函数的多个参数传进去,特别是需要传入较多参数比如5个参数或者更多参数时,这种情况建议直接将这个对象直接传入作为函数参数,这样既可以减少参数的个数,增加了对象间的信赖性,而且这样被调用者需要这个对象的其他属性时可以不用人为的再去修改函数参数。

    \\t\\t

  • \\t

    以函数取代参数:对象调用某个函数,并将所得结果作为参数传递给另外一个函数,而那个函数本身也能够调用前一个函数,直接让那个函数调用就行,可以直接去除那个参数,从而减少参数个数。

    \\t\\t

  • \\t

    引入参数对象:某些参数总是同时出现,新建一个对象取代这些参数,不但可以减少参数个数,而且也许还有一些参数可以迁移到新建的参数类中,增加类的参数扩展性。

    \\t\\t

  • \\t

    移除设值函数(Setting Method):如果类中的某个字段应该在对象创建时赋值,此后就不再改变,这种情景下就不需要添加Setting method。

    \\t\\t

  • \\t

    隐藏函数:如果有一个函数从来没有被其他类有用到,或者是本来被用到,但随着类动态添加接口或者需求变更,之后就使用不到了,那么需要隐藏这个函数,也就是减小作用域。

    \\t\\t

  • \\t

    以工厂函数取代构造函数:如果你希望创建对象时候不仅仅做简单的构建动作,最显而易见的动机就是派生子类时根据类型码创建不同的子类,或者控制类的实例个数。

    \\t\

重构技巧 — 条件表达式

\\

  • \\t

    分解条件表达式:如果有一个复杂的条件语句,if/else语句的段落逻辑提取成一个函数。

    \\t\\t

  • \\t

    合并条件表达式:一系列条件测试,都得到相同的测试结果,可以将这些测试表达式合并成成一个,并将合并后的表达式提炼成一个独立函数,如果这些条件测试是相互独立不相关的,就不要合并。

    \\t\\t

  • \\t

    合并重复的条件片段:在条件表达式的每个分支上有着相同的一段代码,把这段代码迁移到表达式之外。

    \\t\\t

  • \\t

    移除控制标记:不必遵循单一出口的原则,不用通过控制标记来决定是否退出循环或者跳过函数剩下的操作,直接break或者return。

    \\t\\t

  • \\t

    以卫语句替代嵌套条件表达式:条件表达式通常有两种表现形式,一:所有分支都属于正常行为;二:只有一种是正常行为,其他都是不常见的情况。对于一的情况,应该使用if/else条件表达式;对于二这种情况,如果某个条件不常见,应该单独检查条件并在该条件为真时立即从函数返回,这样的单独检查常常被称为卫语句。

    \\t\\t

  • \\t

    以多态取代条件表达式:如果有个条件表达式根据对象类型的不同选择而选择不同的行为,将条件表达式的每个分支放进一个子类内的覆写函数中,将原始函数声明为抽象函数。

    \\t\\t

  • \\t

    引入Null对象:当执行一些操作时,需要再三检查某对象是否为NULL,可以专门新建一个NULL对象,让相应函数执行原来检查条件为NULL时要执行的动作,除NULL对象外,对特殊情况还可以有Special对象,这类对象一般是Singleton.

    \\t\\t

  • \\t

    引入断言:程序状态的一种假设

    \\t\\t

  • \\t

    以MAP取代条件表达式:通过HashMap的Key-Value键值对优化条件表达式,条件表达式的判断条件作为key值,value值存储条件表达式的返回值。

    \\t\\t

  • \\t

    通过反射取代条件表达式:通过动态反射原理

    \\t\

重构技巧 — 案例

\\

前面这多章节内容主要都是理论内容,接下来我们来看看具体的重构案例。

\\

Map去除if条件表达式

\\

关于该技巧的实现方法,上章节有讲述,我们直接看代码案例如下代码所示:

\\

原始的条件表达式代码如下图1所示:

\\

\public static int getServiceCode(String str){\     int code = 0;\     if(str.equals(\"Age\")){\         code = 1;\     }else if(str.equals(\"Address\")){\         code = 2;\     }else  if(str.equals(\"Name\")){\         code = 3;\     }else if(str.equals(\"No\")){\         code = 4;\     }\     return  code;\ }

\\

重构后的代码如下所示:

\\

\public static void initialMap(){\     map.put(\"Age\

整洁代码之道——重构相关推荐

  1. 整洁代码之道 12 迭进 Emergence

    这本书越到后面翻译的越是起飞,这一章的英文标题是 Emergence ,完全不知道这个译者为啥会自造一个词来实现翻译 译者想表达的意思可能是迭代+进化 12.1 通过迭进设计( Emergent De ...

  2. 《整洁代码之道》学习书摘(二)第一章——整洁代码

    第一章 整洁代码 第一章 整洁代码 要有代码 糟糕的代码 混乱的代价 思想流派 我们是作者 童子军军规 前传与原则 小结 学习收获 要有代码 阅读本书(书摘)有两种原因,第一,你是个程序员:第二,你想 ...

  3. 代码整洁之道读书笔记——第一章:整洁代码

    软件质量,不仅仅依赖于项目架构和项目管理,同样重要的是代码质量!!! 序 神在细节之中,其实干什么事都一样,从小到大,一直明白一个道理:细节决定成败! 软件架构在开发中占据重要地位.其次,宏达建筑的最 ...

  4. 《代码整洁之道》第一章 整洁代码 ---为什么需要整洁代码?

    第一章 整洁代码 概述 什么是整洁代码? 开始走向整洁代码 概述 欢迎阅读本栏目的读者,如果你想成为更加优秀的coder,请跟随笔者的观点去解析<代码整洁之道>这本书,相信你会收获颇丰. ...

  5. 代码整洁之道--------整洁代码

    软件质量不但依赖于整体的项目架构及项目管理,而且与代码质量紧密相关.书中有一个观点我觉得很对:代码质量与代码整洁度成正比.干净的代码,既在质量上较为可靠,也为后期的项目维护.迭代奠定了良好的基础. 一 ...

  6. 《代码整洁之道》一之整洁代码的必要性

    1.为什么需要代码 代码是需求的实现,呈现了需求的细节. 编程就是将需求明确到机器可以执行的细节程度,而这种规约就是代码. 总之:代码是我们最终用来表达需求的语言. 2.糟糕的代码 不客气的说:糟糕的 ...

  7. 什么是整洁代码?大咖程序员们这样说

    这是本有关编写好程序的书.它充斥着代码.我们要从各个方向来考察这些代码.从顶向下,从底往上,从里而外.读完后,就能知道许多关于代码的事了. <代码整洁之道> 马丁 著 而且,我们还能说出好 ...

  8. 整洁架构之道--三种经典的编程范式

    本文是<Clean Architecture>--整洁架构之道中关于编程范式相关章节的笔记,首发于公众号「Go 招聘」 前言 之前整理了整洁架构之道这本书前两章节的读书笔记:<cle ...

  9. 优秀程序员眼中的整洁代码

    点击上方蓝字关注公众号 码个蛋第274次推文 看看大神们怎么说 作者:风铃无声江舟听雨 博客:https://www.jianshu.com/u/b525437c0777 有多少程序员,就有多少定义. ...

最新文章

  1. android布局共享,布局共享(如所有ACTIVITY拥有相同的布局部分,比如ACTIONBAR,在BASEACTIVITY中写入布局)...
  2. python自学用什么书好-适合python基础学习的好书籍
  3. Linux入门-shell使用技巧
  4. nodejs-url网址解析的好帮手
  5. 【Boost】boost库asio详解7——boost::asio::buffer用法
  6. c#下不同命名空间的引用(一个project使用另外一个project的名称空间)
  7. 《汇编语言》王爽—实验五详解
  8. Java讲课笔记11:构造方法与this关键字
  9. 数据分析 | 这个新职业年薪高达49w,作为普通打工人的你眼馋了吗?
  10. 虚幻引擎游戏技能系统文档
  11. 计算机硬件中内存的作用是什么,内存是什么 内存条的作用到底是什么
  12. python文件或目录损坏且无法读取,data\sogoupy\verify.ini已损坏且无法读取,啥意思?谢谢?...
  13. 后台管理导航菜单及模板
  14. Telemetry 标准日志接口如何提升运维效率?
  15. 水泥行业工业互联网平台(CCPS)解决方案
  16. ROS系统中编写多个C++文件时,主文件调用其它文件函数或类时出现:对“xxxxxx“未定义的引用问题记录
  17. Hawk-and-Chicken
  18. ZZULIOJ1166: 实数取整(指针专题)
  19. Azure China (6) SAP 应用在华登陆 Windows Azure 公有云
  20. 【硬件】LED灯发光原理总结

热门文章

  1. 无需VR外设,普林斯顿学霸用DeepHand解放你的双手
  2. 使用Kickstart+Apache+Dhcp+Pxe无人值守安装操作系统
  3. 配置OSPF时需要注意的几点
  4. Linux 操作系统原理 — 多处理器架构
  5. VMware 虚拟化编程(13) — VMware 虚拟机的备份方案设计
  6. 拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录
  7. 《深入解析Android 5.0系统》——第1章,第1.2节安装开发包
  8. [黑金原创教程] FPGA那些事儿《数学篇》- CORDIC 算法
  9. 如何利用线框图展示初期产品
  10. 一道Java面试题目——随想+心得