相关阅读:
互联网技术(java框架、分布式、集群)干货视频大全,不看后悔!(免费下载)
转载: http://www.importnew.com/21374.html
google 做的最明智的规定之一就是严格执行 code review。每一个改动在上线之前,都要经过两种形式的 review。首先,团队中的人会进行常规的 review,以确保代码完成了它应该完成的功能。
接下来还会进行可读性层面的 review。顾名思义,它是为了确保代码是可读性高的:是否利于理解和维护?是否符合该编程语言的一些惯例?是否有良好的文档?
Dart 已经开始在 google 内部使用,所以我有幸参与了 n 次上面类型的 code review。作为该语言的设计者,这是一项令人着迷的工作。我可以直接看到人们是如何使用 Dart 的,这对语言的进一步发展很有帮助。在 reivew 的同时,我也能够清晰的了解到那些比较常见的错误和使用最多的特性,我就好像是一个记录本地居民生活的人类学者。
当然,上面说的与本文的主旨无关,这并不是一篇关于 Dart 的文章。本文主要是想讨论我看到过的一些令人抓狂的代码:这些代码的变量命名实在是太尼玛的长了。。。。。
是的,变量的名称可以很短。回想当初 C 语言中外部标识符仅需要由前六个字符来唯一的区分,自动补全功能还没有发明,每次按键盘都像在雪地上坡一样艰难的时候,长的命名就会带来很多问题。不过幸运的是,我们现在生活的世界太美好了,键盘操作变得如此简单。但我们现在似乎走上了另一个极端,我们不应该做海明威,但我们也无需成为田纳西·威廉斯。代码中使用了超长的命名会影响代码的清晰性。同时,超长的变量命名会造成换行,这会影响代码的结构,不易于阅读。
长的类名会使开发者不易声明该类型的变量。
长的方法命名会使它变得晦涩难懂。
长的变量命名不利于代码重用,导致过长的方法链。
我曾见过超过 60 个字符的变量命名,你甚至可以写首诗。别慌,下面我们来看看如何解决这一问题。
选择一个好的命名
命名有两个目标:
清晰:你要知道该命名与什么有关。
精确:你要知道该命名与什么无关。
当一个命名完成上面两个目标之后,其余的字符就是多余的了。下面是我在开发时的一些命名原则:
命名中无需含有表示变量或参数类型的单词
如果使用如 Java 之类的静态类型语言,开发者通常知道变量的类型。由于方法的实现一般都比较简短,所以即便是在查看一个需要推断才知道类型的本地变量,或者在 code review 等静态分析器不可用的情况下,我们也可以通过多看很少的几行代码就能知道变量的类型。所以将类型说明加入到变量名中是多余的,我们应该舍弃匈牙利命名法,如下:
// 不好的:String nameString;DockableModelessWindow dockableModelessWindow;// 改进:String name;DockableModelessWindow window;
String nameString;
DockableModelessWindow dockableModelessWindow;
// 改进:
String name;
DockableModelessWindow window;
特别是对于集合来说,最好使用名词的复数形式来描述其内容,而不是使用名词的单数形式来描述。如果开发者更在乎集合中存储的内容,那么变量命名应当反映这一点。
// 不好的:List<DateTime> holidayDateList;Map<Employee, Role> employeeRoleHashMap;// 改进:List<DateTime> holidays;Map<Employee, Role> employeeRoles;
List<DateTime> holidayDateList;
Map<Employee, Role> employeeRoleHashMap;
// 改进:
List<DateTime> holidays;
Map<Employee, Role> employeeRoles;
这一点也同样适用于方法的命名。方法名不需要描述它的参数及参数的类型–参数列表已经说明了这些。
// 不好的:mergeTableCells(List<TableCell> cells)sortEventsUsingComparator(List<Event> events, Comparator<Event> comparator)// 改进:merge(List<TableCell> cells)sort(List<Event> events, Comparator<Event> comparator)<TableCell> cells)
sortEventsUsingComparator(List<Event> events,
Comparator<Event> comparator)
// 改进:
merge(List<TableCell> cells)
sort(List<Event> events, Comparator<Event> comparator)
这样可以帮助调用者更好的阅读:
mergeTableCells(tableCells);sortEventsUsingComparator(events, comparator);
当然,这只是我个人的看法,欢迎大家一起讨论。
省略命名中不是用来消除歧义的单词
有些开发者倾向于将他们知道的有关这个变量的所有信息都塞到命名里。要记住,命名只是一个标识符:只是告诉你该变量是在哪定义的。并不是用来告诉阅读者所有他们想知道的有关这个对象的详细信息。这是定义应该做的事情的。命名只是让你找到他的定义。
当我看到一个叫 recentlyUpdatedAnnualSalesBid (最近更新的全年销售投标) 的标识符时我会问:
存在不是最近更新的全年销售投标么?
存在没有被更新的最近的全年销售投标么?
存在最近更新的非全年的销售投标么?
存在最近更新的全年非销售的投标么?
存在最近更新的全年销售非投标的东东吗?
上面任何一个问题的回答是“不存在”,就意味着命名中引入了无用的单词。
// 不好的:finalBattleMostDangerousBossMonster;weaklingFirstEncounterMonster;// 改进:boss;firstMonster;
finalBattleMostDangerousBossMonster;
weaklingFirstEncounterMonster;
// 改进:
boss;
firstMonster;
当然,你可能会觉得这有一些过了。比如将第一个例子的标识符简化为 bid,会让人觉得有点模糊不清。但你可以放心大胆的这样做,如果在之后的开发中觉得该命名会造成冲突或不明确,可以添加些修饰词来完善它。反之,如果一开始就取了一个很长的命名,你是不可能在之后重新回来简化它的。
省略命名中可以从上下文获取的单词
我可以在文章中使用”我”,因为读者都知道这是一篇由 Bob Nystrom 所做的博客。我蠢萌的脸就挂在那,我无需不停的说我的名字。写代码也是一样,类中的方法/属性和方法中的变量,都是存在在上下文中的,无需重复。
// Bad:class AnnualHolidaySale { int _annualSaleRebate; void promoteHolidaySale() { ... }}// Better:class AnnualHolidaySale { int _rebate; void promote() { ... }}
class AnnualHolidaySale {
int _annualSaleRebate;
void promoteHolidaySale() { ... }
}
// Better:
class AnnualHolidaySale {
int _rebate;
void promote() { ... }
}
实际上,一个命名嵌套的层次越多,它就有更多的相关的上下文,也就更简短。换句话说,一个变量的作用域越小,命名就越短。
省略命名中无任何含义的单词
我常常在许多游戏开发中看到包含无任何含义的单词的命名,一些开发者喜欢在命名中添加一些看起来有点严肃的单词。我猜可能他们觉得这样做可以让他们的代码显得重要,或者说让他们觉得自己更重要。
实际上,有一些词语并没有实际意义,只是一些套话。比如:data、state、amount、value、manager、engine、object、entity 和 instance。
一个好的命名能够在阅读者的脑海中描画出一幅图画。而将某变量命为 ”manager” 并不能向读者传达任何有关该变量是做什么的信息。它是用来做绩效评估的吗?它是管理加薪的吗?
在命名时可以问一下自己,把这个单词去掉含义是不是不变?如果是,那就果断把它剔除吧。
实际例子 — 华夫饼
为了让大家了解以上的命名原则在实际中如何应用,这有个违法了以上所有原则的反例。这个例子和我实际上review过的一段代码一样令人心碎。。。。
// 好吃的比利时华夫饼class DeliciousBelgianWaffleObject { void garnishDeliciousBelgianWaffleWithStrawberryList( List<Strawberry> strawberryList) { ... }}
class DeliciousBelgianWaffleObject {
void garnishDeliciousBelgianWaffleWithStrawberryList(
List<Strawberry> strawberryList) { ... }
}
首先,通过参数列表我们可以知道方法是用来处理一个 strawberry 的列表,所以可以在方法命名中去掉:
class DeliciousBelgianWaffleObject { void garnishDeliciousBelgianWaffle( List<Strawberry> strawberries) { ... }}
void garnishDeliciousBelgianWaffle(
List<Strawberry> strawberries) { ... }
}
除非程序中还包含不好吃的比利时华夫饼或者其他国家的华夫饼,不然我们可以将这些无用的形容词去掉:
class WaffleObject { void garnishWaffle(List<Strawberry> strawberries) { ... }}
void garnishWaffle(List<Strawberry> strawberries) { ... }
}
方法是包含在 WaffleObject 类中的,所以方法名中无需 Waffle 的说明:
class WaffleObject { void garnish(List<Strawberry> strawberries) { ... }}
void garnish(List<Strawberry> strawberries) { ... }
}
很明显它是一个对象,任何事物都是一个对象,这也就是传说中的“面向对象”的含义,所以命名中无需带有 Object:
class Waffle { void garnish(List<Strawberry> strawberries) { ... }}
void garnish(List<Strawberry> strawberries) { ... }
}
好了,这样看起来好多了。
以上就是我总结的相当简洁的命名原则。可能有些人会觉得无需在命名上耗费太多的精力,但我认为命名是开发过程中最基本的任务之一。
感觉变量或者方法的命名,看似简单,实际很难,特别是想一个简洁明了可读性高的命名。自己也经常用什么 data、xxxlist 来命名,作者说的挺对的,前者没什么意义,后者又有点啰嗦。不过对于集合类型的变量,统一用名词复数命名容易混淆。举个例子对于 Apple 这个类来说,可能存在 List 和 Map 两种集合类型的变量。个人觉得对 List 类型的变量可以采用名词复数来命名,Map 类型的变量可以采用 valueByKey 格式来命名,比较容易区分。
妈妈常教导我,让我养成良好习惯。这样长大才能成为一个有用的人。良好的习惯是尊敬师长这样长大才能成为一个有用的人。良好的习惯是尊敬师长,爱护同学,对人有礼貌;是不粗心,做事情不拖拉;还是爱护公物,不浪费粮食。为什么呢?因为拥有良好习惯,做一个品德高尚的人,懂得尊重别人,才会得到别人的尊重。我要努力地做到这些。我有一些坏习惯,有时候学习很粗心,把一些会做的题做错。在生活上,也很粗心,有一次早上起床居然穿反了衣服。我吃饭很慢,有的时候还剩饭。我还起床磨蹭,本来应该迅速地穿好衣服,但是,我总是磨磨蹭蹭地,速度很慢。“我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!” 在上幼儿园以前,我什么也不会干,就连穿衣服也是妈妈给我穿好,就要上幼儿园了,这样可不行,妈妈锻炼我要学会自己穿衣服。 有一天,妈妈把衣服摆在我面前,开始让我自己穿。一开始。我又哭又叫就是不穿,还把衣服扔的满地都是,然后坐在地上开始大哭,等了好长时间,妈妈还是不理我,我只好自己乖乖的把衣服穿好, 一出了房间门,妈妈就笑了起来,再看看我的衣服,毛衣和裤子都穿反了,我赶紧回房间又重新穿了一遍,这次穿好了,拿起外套,可是外套的扣子又扣不上了,扣子可调皮了,好像故意和我作对,我把扣子往扣眼——人类邪恶的根源;爱情——幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话:幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话幸福和光明的源泉。我一直在这些思想的舞台上徘徊。突然我发现两个身影从我面前经过,坐在不远的草地上。这是一对从农田那边走过来的青年男女。农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话“亲爱的!擦干你的眼泪,至高无上的爱情已经打开了我们的眼界,使我们成了它的崇拜者。是它,
妈妈常教导我,让我养成良好习惯。这样长大才能成为一个有用的人。良好的习惯是尊敬师长这样长大才能成为一个有用的人。良好的习惯是尊敬师长,爱护同学,对人有礼貌;是不粗心,做事情不拖拉;还是爱护公物,不浪费粮食。为什么呢?因为拥有良好习惯,做一个品德高尚的人,懂得尊重别人,才会得到别人的尊重。我要努力地做到这些。我有一些坏习惯,有时候学习很粗心,把一些会做的题做错。在生活上,也很粗心,有一次早上起床居然穿反了衣服。我吃饭很慢,有的时候还剩饭。我还起床磨蹭,本来应该迅速地穿好衣服,但是,我总是磨磨蹭蹭地,速度很慢。“我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!我打算在这学期里,改掉这些坏习惯。早上起来,迅速地穿好衣服,不拖拉。学习不粗心,仔细完成每一道题。吃饭的时候,要很快的把饭吃完,不剩饭。我要从一点一滴做起,逐渐养成良好习惯。我相信自己一定能成为一名品学兼优的好学生!” 在上幼儿园以前,我什么也不会干,就连穿衣服也是妈妈给我穿好,就要上幼儿园了,这样可不行,妈妈锻炼我要学会自己穿衣服。 有一天,妈妈把衣服摆在我面前,开始让我自己穿。一开始。我又哭又叫就是不穿,还把衣服扔的满地都是,然后坐在地上开始大哭,等了好长时间,妈妈还是不理我,我只好自己乖乖的把衣服穿好, 一出了房间门,妈妈就笑了起来,再看看我的衣服,毛衣和裤子都穿反了,我赶紧回房间又重新穿了一遍,这次穿好了,拿起外套,可是外套的扣子又扣不上了,扣子可调皮了,好像故意和我作对,我把扣子往扣眼——人类邪恶的根源;爱情- 。幸福和光明的源泉我一直在这些思想的舞台上徘徊突然我发现两个身影从我面前经过,坐在不远的草地上这是一对从农田那边走过来的青年男女农田那边有农民的茅舍。在一阵令人伤心的沉默之后,随着一声长叹,我听见从一个肺痨病人的嘴里说出了这样的话: “ 亲爱的!擦干你的眼泪,至高无上的爱情已经打开了我们的眼界,使我们成了它的崇拜者。是它,
看完本文有收获?请转发分享给更多人
欢迎关注“互联网架构师”,我们分享最有价值的互联网技术干货文章,助力您成为有思想的全栈架构师,我们只聊互联网,只聊架构,不聊其他!打造最有价值的架构师圈子和社区。
本公众号覆盖中国主要首席架构师,高级架构师,CTO,技术总监,技术负责人等人群。分享最有价值的架构思想和内容。打造中国互联网圈最有价值的架构师圈子。
长按下方的二维码可以快速关注我们
如想加群讨论学习,请点击右下角的“ 加群学习 ”菜单入群