这是哪个天才写的代码?真是神逻辑!!!

告诉我你家的地址,我保证不打你……

大哥,你真会取名!你的英语是体育老师教的吗?

这八层嵌套是谁写的?老板,我屏太小,能换个显示器吗……

在你们多年的程序生涯中,不知是否曾经有过这样的感受!如果有,那恭喜你一定遇到奇葩代码了!如果没有,我想也许只有两个解释是合理:

  1. 经历的项目太少;
  2. 你经历过的团队都是非常优秀的团队!

作为一个五年程序员,不长也不短!以上各种酸甜苦辣的感受我都曾有过,各种奇葩的代码也曾遇到过!面对这些奇葩的代码,也操刀过,我将分享一些我自己的感悟和处理方法。

通过本场 Chat,你将了解:

  • 奇葩代码产生的原因
  • 重构你的奇葩代码
  • 最普遍有效的重构方法
  • 代码的整洁之道

我的嗅觉是灵敏的

这是哪个天才写的代码?真是神逻辑!!!

告诉我你家的地址,我保证不打你……

大哥,你真会取名!你的英语是体育老师教的吗?

这八层嵌套是谁写的?老板,我屏太小,能换个显示器吗……

当你有上面这些感受时,恭喜你,你一定嗅到了代码的坏味道!

《重构:改善既有代码的设计》一书中提到:当闻到代码的坏味道时,就应该重构你的代码。那什么是代码的话味道呢?这里列举一些最常见的案例:

  • 重复代码
  • 过长函数
  • 过大的类
  • 过长参数列
  • 糟糕的名称
  • 过多的注释
  • 无穷的嵌套
  • 成堆的 switch... case... 或 if... else...

什么叫重构

重构有两种解释,一种是作名词的解释,一种是作动词的解释。

名词:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

动词:使用一系列重构手法,在不改变软件可观察行为的前提下,调整软件的结构。

为何要重构

车子脏了就得洗,坏了就得修,报废了就得换。

程序也一样,不合需求就得改,难以跟上业务的变更就得重构,实在没法改了就得重写。

一个项目中,团队成员的技术水平参差不齐。有一些工作年限比较低,技术水平比较差的成员写的代码质量比较差,结构比较混乱,这时就需要对这部分代码进行适当的重构,使其具有更高的可重用性。

一个软件运行时间比较长,被多代程序员进行修修补补,使得这个软件的代码非常的臃肿而庞杂,维护成本非常高。因此,也需要对这个软件进行适当的重构,以降低其修改成本。

要进行代码重构的原因,总结一下,常见的原因有以下几种:

  • 重复的代码太多,没有复用性;难于维护,需要修改时处处都得改;
  • 代码的结构混乱,注释也不清晰;没有人能清楚地理解这段代码的含义;
  • 程序没有拓展性,遇到新的变化,不能灵活的处理;
  • 对象结构强耦合,业务逻辑太复杂,牵一发而动全身,维护时排查问题非常困难;
  • 部分模块性能低,随着用户的增长,已无法满足响应速度的要求。

这些导致代码重构的原因,称为代码的坏味道,我这称为脏乱差,这是代码层面的原因(或说表象)。

而代码是人写出来的,这些脏乱差的代码是怎样形成的呢?大概有以下几种人为的因素

  • 上一个写这段代码程序员经验不足,水平太差;或写代码时不够用心;
  • 奇葩的产品经理提出奇葩的需求;
  • 某一个模块业务太复杂,需求变更的次数太多,经手的程序员太多。每个人都在一个看似合适的地方,加一段看似合适的代码。到最后没人能之完完整整地看懂这段代码的含义。

什么时机重构

代码的重构应该贯穿于整个软件开发过程始终,没须要单独拿出一块时间进行,只要你闻到代码的坏味道,即可进行。我们可以遵循 Rule Of Three 原则(也叫三次法则)来进行重构:事不过三,三则重构,这一原则的大体思想是:

这个准则表达的意思是:第一次去实现一个功能尽管大胆去做;第二次做类似的功能设计时会产生反感,但是还是会去做;第三次还要实现类似的功能做同样的事情时,就应该去审视是否有须要做这些重复劳动了,这个时候就应该去重构你的代码了——把重复或相似功能的代码进行抽象,封装成一个通过的模块或接口。

想了解更多关于设计原则的知识,请参见:《谈谈我对设计原则的思考》。虽然重构可以随时随地的进行,但还量需要一些触发点来触发你去做这一件事。这些触发点主要有以下几个:

添加功能时

当添加新功能时,如果发现某段代码改起来特别困难,拓展功能特别不灵活。就要重构这部分代码了,使添加新特性和功能变得更容易。在添加新功能时,只梳理这部分功能相关的代码;一直保持这种习惯,日积月累,我们的代码会越来越干净,开发速度也会越来越快。

修补错误时

在你改 Bug,查找定位问题时,发现自己以前写的代码或者别人的代码设计上有缺陷(如扩展性不灵活),或健壮性考虑的不够周全(如漏掉一些该处理的异常), 导致程序频繁出问题。此时就是一个比较好的重构时机。

可能有人会说:线上问题出来时根本就没那么多时间允许去重构代码。我想说的是:只要不是十万紧急的高危问题,请尽量养成这种习惯。 持续一段时间后,你会发现代码中的坑逐步被填平,欠下的技术债务也会越来越少。

复审代码时

很多公司会有 Code Review 的要求,Code Review 的好处是能有效地发现一些潜在的问题(所谓当局者谜,旁观者清);有助于团队成员进行技术的交流和沟通。在 Code Review 时发现程序的问题,或设计的不足,这又是一个重构的极佳时机,因为 Code Review 时,对方往往能提出一些更好的建议或想法。

如何重构代码

上面讲解了什么时候该重构,怎么进行重构,这又是一个重要的问题。下面将介绍一些最常用和实用的重构方法,下面的这些方法针对各种编程语言都实用 。

重命名

这是最低级、最简单的一种重构手法(现在的集成 IDE 都特别智能,通过 Rename 功能一键就能搞定),但并不代表他的功效就很差。

你有没有见过一些特别奇葩、无脑、或具有误导性的变量名、函数名、类名吗?如下面这样的:

# 下面的例子改编自网上讨论比较火的几个案例# Demo1correct = False# 嗯,这是对呢?还是错呢?# Demo2from enum import Enumclass Color(Enum):    Green = 1   # 绿色    Hong = 2    # 红色# 嗯,这哥们是红色(Red)的单词不会写呢,还是觉得绿色(Lv)的拼音太难看呢?# Demo3def dynamic():    pass    # todo something# 你能想到这个函数是干嘛用的吗?其实是一个表示“活动”的函数。这英语是数学老师教的吗~

如果有,果断把它改掉!一个良好的名称(变量名、函数名、类名),能让你的代码可读性立刻提高十倍。在下一小节“代码整洁之道”会继续讲解程序取名的技巧和原则。

函数重构

提炼函数

有没有见过一函数一千多行的代码?如果有,那恭喜你!前人给你留了一个伟大的坑等着你去添埴。这种代码是极其难以阅读的,所以你需要对他进行拆分,将相对独立的一段段代码块拆分成一个个子函数。这一过程叫函数的提炼。

去除不必要的参数

函数体不再需要某个参数,果断将该参数去除。尽量不要为未来预留参数(需要用到的时候再加),除非你很确定即将要用到它。

用对象取代参数

你有没有见过有十几个参数的函数?这种函数,即使是天才也不太容易能记住每一个参数,往往是看到后面忘了前面。这个时候可以定义一个参数类,类中成员定义为函数需要的各个参数;调用函数时将这个类的对象传入即可,函数体内可通过这个对象取得各个属性。

查询函数和修改函数分离

我们在《谈谈我对设计原则的思考》一文中讲到 CQS 原则,根据这一原则将查询函数和修改函数分离。

隐藏函数

一个类方法,如果不被任何其他类使用,或不希望被其他类使用。应该将这个方法声明为private,对外部进行隐藏。

重新组织数据

用常量名替换常量值

有一个字面值,带有特别的含义,而且可能在多个地方被用到。创建一个常量(或枚举变量),并根据其含义为它命名,将具体的字面数值替换为这个常量。

用 Getter 和 Setter 方法代替直接方法

尽量避免直接访问类的成员属性,将类的成员属性声明为 private,然后定义 public 的 Getter 和 Setter 方法来访问这些属性。

用对象取代数组

有一个数组(array),其中的元素个各自代表不同的东西,用对象替换数组。对于数组中的每个元素,以一个值域表示。

用设计模式改善代码设计

数据结构的重构和函数的重构都是相对基础的重构方法。有一些代码,类的结构及类间的关系本身就不太合理,这时就要用设计模式的思想重构设计这些类间的关系。就需要我们对事物和逻辑有一定的抽象思维,也就是面向对象的思想。一个大致的思考方向是:

  1. 把具有相似功能的类归纳在一起,并抽象出一个基类,让这些类继承自这个基类(也称为父类)。
  2. 把子类都使用的方法和属性提炼到父类,并声明为 protected(部分方法可能要声明为 public)。
  3. 不同体系的类之间(如动物和食物),依赖抽象和接口编程,即是依赖倒置原则。

这一些方法,需要长期的经验和总结,不能一蹴而就!需要认真学习和领悟设计模式和设计原则,如果你感兴趣,可学习我的课程《从生活中领悟设计模式(Python)》。

代码整洁之道

命名的学问

程序中的命名包括变量名、常量名、函数名、类名、文件名等。一个良好的名称能让你的代码具有更好的可读性,让你的程序更容易被人理解;相反,一个不好的名称不仅会降低代码的可读性,甚至会有误导的作用。良好的名称应当是可读的、恰当的并且容易记忆的。 好的命名还可以取代注释的作用,因为注释通常会滞后于代码,经常会出现忘记添加注释或注释更新不及时的情况。

语义相反的词汇要成对出现

正确的使用词义相反的单词做名称,可以提高代码的可读性。比如 “first/last” 比 “first/end” 通常更让人容易理解。下面是一些常见的例子:

第1组 第2组 第3组 第4组
add / remove begin / end create / destory insert / delete
first / last get /set increment / decrement up / down
lock / unlock min / max next / previous old / new
open / close show / hide source / destination start / stop
计算限定符作为前缀或后缀

很多时候变量需要表达一些数值的计算结果,比如平均值和最大值。这些变量名中会包含一些计算限定符(Avg、Sum、Total、Min、Max),在这种时候,可以使用限定符在前或者限定符在后两种方式对变量进行命名,但不要在一个程序中同时使用两种方法。如可以使用 priceTotal 或 totalPrice 来表达总价,但不要在一段代码里两种同时使用。虽然这可能看起来微不足道,但确实这样做可以可避免一些歧义。

变量名要能准确地表示事务的含义

作为变量名应尽可能全面,准确地描述变量所代表的实体。设计一个好的名字的有效方法,是用连续的英文单词来说明变量代表什么,命名中一律使用英文单词,不要使用汉语拼音,更不要使用汉字。

变量的目的 好的名字 不好的名字
Current time currentTime ct, time, current, x
Lines per page linesPerPage lpp, lines, x
Publish date of book bookPublishDate date, bookPD, x
用动名词命名函数名

函数名通常会描述在某个对象上的某个操作,因此采用 动词 + 对象名 的方式来做为函数名的命名约定,如 uploadFile()。

使用面向对象的语言时,在一些描述类属性的函数命名中加上类名是多余的,因为对象本身会包含在调用的代码中。例如使用 book.getTitle() 而不是 book.getBookTitle(),使用 report.print() 而不是 report.printReport()。

变量名的缩写

习惯性缩写:

始终使用相同的缩写。例如,对 number 的缩写,可以使用 num 也可以使用 no,但不要两个同时使用,始终保证使用同一个缩写。

使用的缩写要可以发音:

尽量让你的缩写可以发音。例如,用 curSetting 而不用 crntSetting,这样可以方便开发人员进行交流。

避免罕见的缩写:

尽量避免不常见的缩写。例如,msg(message)、min(Minmum) 和err(error) 就是一些常见的缩写,而 cal(calender) 大家就不一定都能够理解。

了解更多

想了解更多关于设计原则、代码重构、设计模式的知识,欢迎阅读《从生活中领悟设计模式(Python)》课程,此课程适合具有一定编程基础又渴望提升自己编程技能的人,此课程如武功秘籍一般力求用最通俗的语言阐述最难懂的概念;用最简单的语法实现最复杂的逻辑;用最短小的代码写出最强悍的程序!

此课程分三卷内容:

第一卷:基础篇(第 01~21 课),19 种常用设计模式单独章节讲解 + 剩余 4 种模式合集(会有 1~2 篇的篇幅);

第二卷:进阶篇(第 22~24 课),是基础设计模式的衍生,也是各大编程语言中非常重要而常见的种编程机制;

第三卷:经验篇(第 25~27 课),将会分享我对设计原则、设计模式、项目重构的经验和看法。

最后的问题

你见过最奇葩的代码是怎样的?你是怎样处理这些奇葩代码的?

欢迎底下评论留言!!!


本文首发于GitChat,未经授权不得转载,转载需与GitChat联系。

阅读全文: http://gitbook.cn/gitchat/activity/5bbcb33d5727064fd9d29ac5

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App , GitChat 专享技术内容哦。

奇葩代码虐我千百遍,我却待它如初恋相关推荐

  1. 算法虐我千百遍,我待算法如初恋

    转自:算法虐我千百遍,我待算法如初恋 算法虐我千百遍,我待算法如初恋. 学习方法  1) 把所有经典算法写一遍  2) 看算法源码  3) 加入算法学习社区,相互鼓励学习  4) 看经典书籍 基本数据 ...

  2. Maven 虐我千百遍,我待 Maven 如初恋 侵删

    前言 在如今的互联网项目开发当中,特别是Java领域,可以说Maven随处可见.Maven的仓库管理.依赖管理.继承和聚合等特性为项目的构建提供了一整套完善的解决方案,可以说如果你搞不懂Maven,那 ...

  3. 客户虐我千百遍,我待客户如初恋

    误闯误撞,进入了IDC行业,从事了服务器租用及托管的销售工作.很多人问我,什么是IDC,什么是服务器?我知道我解释的过于专业那么会有更多的问题,毕竟自己也不是百科全书,只好简答回复"没有显示 ...

  4. 代码虐我千百遍,我待代码如初恋。

    团队里的程序员张三丰要离职,领导让你接手他的工作,叮嘱你一定要尽快掌握张三丰的代码.你的心儿扑通扑通地跳动,你的脑海里萦绕着三个选择:是拒绝呢,还是拒绝呢,还是拒绝呢?你强颜欢笑但实际上心烦意乱怨气纵 ...

  5. 【算法虐我千百遍,我待ACM如初恋】Hail Algorithm,Hallelujah ACM!!

    (谨向余光中先生致敬) 鼠标轻划标题上的永恒常量 有人好奇的看着你,朋友 太过熟悉的窗口,人们不知 那无垠的循环中,你的叮咛 人心中总有魔兽红警反恐撸啊撸 却不知你富有魅力的救赎 三百分钟即创世 何止 ...

  6. Maven虐我千百遍,我待Maven如初恋

    前言 在如今的互联网项目开发当中,特别是Java领域,可以说Maven随处可见.Maven的仓库管理.依赖管理.继承和聚合等特性为项目的构建提供了一整套完善的解决方案,可以说如果你搞不懂Maven,那 ...

  7. HashMap 虐我千百遍,我待 HashMap 为初恋

    HashMap.HashTable.ConcurrentHashMap 这一套感觉今年面试都不怎么问了,场景题越来越多,求职的门槛越来越高,这种常见的面试题问出来大概率就是要送波分了. 1. 讲讲 H ...

  8. 数学歌单:数学虐我千百遍,让我至今没初恋

    提起数学,很多人爱之恨之,一言难尽.数学总被人贴上严肃和枯燥的标签,其实任何事物都不能将其绝对化,数学也是如此,数学也有其美的一方面,只是缺少了发现数学美的眼睛.数学和音乐当然也有交叉点,这些歌曲你都 ...

  9. Maven虐我千百遍,我待Maven如初恋!

    来源 | 张丰哲 jianshu.com/p/20b39ab6a88c 互联网开发,特别Java领域,可以说Maven随处可见.Maven的仓库管理.依赖管理.继承和聚合等特性为项目构建提供了一整套完 ...

最新文章

  1. win7、win10进程pid4占用80端口的解决办法
  2. nvidia 程序安装失败
  3. 2021年的最后7天,和我的伙伴们合个影吧
  4. android之phonegap入门
  5. MySql【超简单】清空部分表的数据
  6. Window下利用命令行提交代码到GitHub
  7. Oracle中rownum用法总结
  8. No_16_0224 Java基础学习第五天
  9. netzapper操作
  10. 简述delete与delete[]的区别
  11. struts2进阶篇(3)
  12. 图像算法八 —— 多种边缘检测算法(Sobel算子、Isotropic Sobel算子、Roberts算子、Prewitt算子、Laplacian算子、Canny算子)介绍及比较
  13. MATLAB柱状图(数据可视化)
  14. 毕业设计 嵌入式 指纹识别考勤系统设计与实现
  15. Linux 执行kettle ktr脚本
  16. 【它来了】搭载龙芯3A4000的国产办公电脑-桌面型机箱来了~
  17. 修复Adobe Flash Player
  18. 视频文件服务器加速,视频CDN,视频点播加速,视频点播CDN加速,CDN流媒体点播加速_速网云计算CDN服务商·卓越的互联网业务平台提供商...
  19. 【小技巧】Linux安装matlab教程
  20. 数据结构5 :DataFrame(Pandas库)

热门文章

  1. 搜狐号按作者火车头采集规则
  2. 一篇让小白彻底搞懂性能调优!
  3. python运维看什么书_学习Python在Linux运维上的应用应该看哪些书 什么样的学习路线...
  4. 【mysql数据库】通过python 3.7 爬虫获取企查查公开的企业信息,并记录到数据库
  5. SQL Server 学习5(随机数,整数,编号,序号)
  6. ffmpeg中vf与filter_complex的区别
  7. 单片机通用配置(基于STC12C5A60S2)
  8. Gentoo 完整的USE参数清单中文详解(转)
  9. 力扣(674.160)补8.30
  10. wireshark怎么抓包、wireshark抓包详细图文教程