Clean Code 告诉你什么是好代码

前言

最近在团队推行Code Review,遇到一个头痛的问题。当向伙伴的代码提一个comment时,他们不解为什么需要这样改。细细想来,是他们不知道何为好代码,也不知道自己的代码有哪些 "坏味道"。因此,分享了几期Clean Code,团队受益良多,故成此文。

Clean Code

由于Clean Code篇幅较长,故先安排如下我认为较为重要的几点:

  • 命名
  • 函数(方法)
  • 注释
  • 对象、数据结构

命名

命名有许多规则,但总结起来就是 “有意义” 才是硬道理。

名副其实

Int d;//逝去的时间

这句代码的问题在于d没有表达好逝去的时间这个概念,故需要注释。请记住“名副其实就不需要注释”

Int elapsedTime;

再来看个例子


谁都很难猜出其意义,看看小优化后的结果

基本看清了意义,这就是命名的重要性。细心的朋友还会发现这段代码的一些瑕疵 :这里的4是什么鬼?习惯性我们管它叫“魔法数字”

还是觉得有点问题,再优化

对比下最早的代码,相信你会有感觉了。

避免误导

生活中的场景也常出现在Code中,看下图,你的Code是否也出现这样的尴尬呢?那就Make it clean



是否傻傻分不清了呢? 再来个

accountList

我知道你想说,这有什么问题。是的,如果你不是做Java开发,不会知道链表叫List,所以如果你不是用链表存储account,请不要用其修饰,或许这个时候你使用acountGroup会更好些。
该点需要在具体开发环境下因地制宜

有意义的区分

Product
ProductInfo
ProductData

可以想象下,当一个项目中同时出现以上三个类的时候,你是如何区分开的,反正我是没有这个能力。类似的还有

game
theGame
name
nameString

分享时,伙伴说nameString有什么问题。我反问说难道你的名字会是Float型的?你懂了吧。

前缀

m_desc

有人提出加m前缀表示该变量为私有变量。
我想说:你的变量很多?需要区分私有的还是公有的?如果你的变量很多,那就要想想是不是没设计好类,没有遵循单一职责原则,另外私有和公有变量编译器会帮忙高亮显示区分的,不需要自己来区分(若某些编译器无此特性,怪编译器去)。

命名惯性

命名需要注重词性
类名:名词 or 名词短语
方法名: 动词 or 动词短语

每个概念对应一个词

在一个模块中不要使用两个相似的概念来表达不同的操作。我在一份代码中看到过一个类中同时出现以下三个词打头的方法

fetch
get
retrieve

请问那个才是真的获取值的方法?我实在分不清。

使用领域名称

使用领域命名能让伙伴更明白你的程序结构(关于领域这个概念,不熟悉的可以看下一本书叫 《领域驱动设计》,俗称DDD)
举个例子,比如你使用访问者模式来构建用户系统,那么

AccountVisitor

就显得明确、易懂

抵制缩写诱惑

缩写需要注意,适当的缩写是可以的,但是要保证缩写后的词语仍然能表达其本意。举个有意思的例子

ABCDEFG

这也是个缩写,但是乍看这个真不知道是什么的缩写,直接公布答案吧

小结

命名是永恒的难题,我提几个建议吧

  • 多看开源代码,积累好的用词
  • 不懂的词就查下词典,好过你自己想的
  • 做个自己的开源项目,让别人给你建议
  • 做好积累、再积累、还是积累
一些借鉴词

函数(方法)

函数的第一条规则是要短小,第二条规则还是要短小。

短小

那到底多短合适呢?历史上出现过几个标准

  • 一屏
  • 100行
  • 50行
  • 20行
    有人问我为什么会差这么多,我的回答是:以前的屏幕分辨率那么低,一屏也就20-50行之间吧,所以以前一屏的说法也是合理的。
    对于行数,行业没有一个固定的标准。我所知道的Oracle建议是50行,Bob大叔的建议是20行。

代码短小,好处自然很多。

  • 单元测试覆盖率高
  • 每个函数一目了然,只做一件事
  • 有利于函数中的代码都在同一个抽象层级

只做一件事

函数应该做一件事。做好这件事。只做一件事。
那么如何判断只做一件事?


请问这个函数做了几件事?伙伴的答案是

1.判断是否为测试页面
2.加入测试数据
3.渲染页面

你的答案是多少呢?其实答案是只做了一件事,主要是没有看清
一件事 OR 一件事的多个步骤,关于这点,大家要好好体会。

另外一个判断是否只做一件事的好方法: 是否能再次分离出新函数

同一个抽象层级

关于层级,比较难讲明,直接看例子吧



再看一个版本



你会发现看第二个版本的代码,明显舒服很多。因为第二的版本的三句代码都在同一个层级。而第一个版本的代码中的第一句是设置roundView的某个属性,但是最后一句却是在设置bubbleView,层级不同(roundView与bubbleView才是同层级)

使用描述性名称

如果长一点的名称可以更加清晰,不要犹豫,用清晰的吧(注意是要有意义的)

calculate
calculatePrice

相比起来calculatePrice就好很多。
再来看个例子

addComment
addCommentAndReturnCount

你不是说长一点更清晰吗,那addCommentAndReturnCount很好吧。
关于这点大家要注意,如果你需要用and、or之类的介词来修辞函数时,要考虑下你是否违背了单一职责原则

参数个数

0个最好,
1个次之,
2个还行,
3个以上不是太好了。
参数与函数名位于不同的抽象层级,它要求你必须了解目前并不特别重要的细节。
解决办法有许多,比如某些场景可使用DTO

嵌套层次、分支过多


嵌套、分支过多会让代码变得很难理解,解决的办法有如下:

  • 卫语句
  • do-while,引入break
  • if-else if-then
  • 提取函数
  • 以子类取代类型代码
  • 以多态取代条件式
  • ...
    具体可根据项目特点选用

分割指令与查询


set这个函数很不明确的是到底是设置成功了返回true,还是名字存在返回true,但真正的问题在于,它是个指令但是掺杂了查询的功能。



将查询和命令分离后,代码便清晰很多了。

小结

如何写出好的函数

  • 先写对的,再写好的
  • 对 =》 单元测试 =》识别坏味道 =》重构

注释

“别给糟糕的代码加注释 — 重新写吧。” –Brian & P.J.
“注释总是一种失败” –Bob

用代码来阐述



感受两段代码会发现代码即注释的美

坏注释

先来看看什么是坏的注释

喃喃自语

这注释绝对是给自己看的

多余的注释

解释跟没解释一样,不如代码来的简单明了

误导性的注释

你在误导吧

循规式注释

这个一定要注意,循环式的注释完全多余(除了做sdk、开源)

括号后的注释

如果括号后需要注释,只表明你这段代码太长了,需要做的不是加注释,而是将它变短。

归属于署名

Git、SVN知道是你提交的,不用这样刷存在感

注释掉代码

注释掉的代码,只会让修改你代码的人蒙圈,如果你觉得这段代码有可能以后会用,也不用担心,Git、SVN会帮你找回来

信息过多

面向对象讲究,暴露操作,隐藏实现,如果你还要注释这些信息,表示你没有封装好。这些信息,可考虑放个链接或者其他的简短提示,太长的注释,别人懒得读、也难读懂


好注释

看了那么多坏注释,来看看什么是好的注释

法律信息
提供信息
对意图的注释
阐释
警示
TODO注释
放大

对象、数据结构

数据抽象


将变量设置为私有(Private),主要是不想让其他人依赖这些变量。所以,不要随便给变量添加赋值方法和取值方法(set/get方法),这样其实是把私有变量公之于众。
隐藏变量和实现,并不是在变量与外界之间放一个函数层那么简单。隐藏关乎抽象。
类并不简单地用赋值方法和取值方法将其变量推向外间,而是暴露抽象接口,以便用户无需了解数据的实现而能操作数据本体。
要以什么方式呈现对象所包含的数据,需要做严肃的思考。随便加赋值方法和取值方法,是最坏的选择。

数据、对象的反对称性

前者是一种过程式代码,后者是面向对象式代码。我们会发现假如要添加一个新形状的话,后者绝对是不错的选择,因为以上代码都不需要修改,只需写一个新形状类,这符合“开放--封闭”原则。然而假如添加一个计算周长的功能的话那就杯具了,因为这样子每个形状类都得改动。但是假如是用过程式代码的话只需要添加一个新函数。

过程式代码(使用数据结构的代码)便于在不改动既有数据结构的前提下添加新函数。
面向对象代码便于在不改动既有函数的前提下添加新类。
一切都是对象只是一个传说

组织

  • 公共静态变量
  • 私有静态变量
  • 私有实体变量
  • 公共函数
  • 私有函数
    自顶向下原则
    这里为什么没有写公有实体变量是因为,其不建议出现在代码中。

短小

函数的短小标准是行数,那类是什么呢?答案是职责
类需要遵循单一职责原则

内聚


如以上代码,内聚性高,除了size方法外,其他方法都使用了两个实例变量。
内聚:模块内部各个元素彼此结合的紧密程度(类中方法和变量间的结合程度)
保持内聚会得到许多短小的类
当一个类丧失内聚性时我们应当拆分它

总结

Clean Code能帮助团队构建代码质量体系,有助于开发的各个环节(静态分析、持续集成、Code Review...)。当然,对个人的能力提高也很有好处,建议大家都应该熟悉。等团队Code Review一段时间后,有其他收获的话,再给大家分享。
预祝大家国庆节快乐!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容