怎样编写人们容易阅读的代码?

有一句广为流传的话:代码是写给人看的,而不是为了给机器执行。然而,编写人类易于阅读的代码,这说起来容易做起来难。这件事需要花费几年时间去学习,花费几十年才能掌握。我也许可以提供一个捷径:像一名教育者一样思考代码质量。

“捷径”这个词表达的能力很强大。这并不是一个捷径。但我认为,这种观点很重要。AlanKay说过,观点抵得上80点智商。

了解你的受众Rails

在工作中,我们使用Rails、Node和Vue。主应用程序是用Rails写的,前端的一部分是用Vue写的,然后我们还用Node编写了一些lambda函数。在理想世界中,Rails人员写Rails,Node人员写Node,Vue人员写Vue,但实际情况并非如此。实际上,我们会遇到这样的情况:Node和Vue人员不得不阅读或编写Rails代码。

委婉地说,Rails是一个古怪的框架,会发生很多奇怪的事情,约定大于配置。例如,如果你看到如下代码:

复制代码

Rails会自动在app/views/api/foo/bar/baz内部寻找或来作为响应。但如果你不是一个Rails开发者你不会知道这些!你所看到的只是一个空方法,它似乎什么也没做!更重要的是,你没办法搞明白。答案不是隐藏在一些父类或mixin中,而是藏在这种部落知识的书中。

我不确定这个controlleractions的示例是不是一个好例子。实际上,这是你很快就能学会的,或者你团队中的某人可以马上发现并帮助你的。但是在其他一些情况下,你可以用Rails做一些古怪的事情,而只有那些正好掌握这些部落知识的人能够理解。

当你在一个拥有经验丰富的Rails专家的团队工作中时,这不是个问题。事实上,这些古怪的东西能够帮助Rails专家变得更高效。但是,如果你工作在一个对Rails都是新手的团队中时,这些菜鸟绝对会陷入绝望和沮丧中。

这就是需要像一名教育者一样思考的地方。假设你是一名教授。如果你站在一小群博士面前,举办一个高度专业化、集中化的研讨会,你可以使用花哨的术语等而不用担心这些术语超出观众的认知。但是如果你发现自己站在一座演讲厅中面对一群本科生,那么,使用这些术语就不是一个明智的选择。

对于Rails也是这样。问题不在于某样东西是“最佳实践”还是“Rails编程方式”,而是在于它对你的受众是否有意义。

Angular

我过去犯过这个错误。在以前的一家公司,我们使用Rails、Angular和Python。我是那个“使用Angular的家伙”。团队的其他人大部分都是Rails人员。

我对于自己对directives的使用感到少许得意。但我的上司叫我停止使用这些东西,坚持使用正常的controllers。他甚至提到,他的理由是因为这是大部分软件开发人员如果进入一个代码库期待看到的编程方式。

ELI5

在《函数式编程为什么重要》一书中,EricNormand谈到了一个有关游戏树的程序。他说,在大学里,他用一堆for循环写过一个类似的程序。然后,他谈到了一篇论文作者采取的方法:

这说明了一些问题,因为EricNormand是一名函数式编程方面的专家。如果你的代码过于简洁,以至于即使是领域专家也很费劲才能理解它,那么这可能不是你应该追求的目标。在同一期播客中,Normand反思函数式编程语言/代码是否过于简洁。

这让我想起EliezerYudkowsky在《!》中写的有些东西:

我认为在编写代码时记住这一点是一件好事。

降低水平?

“了解你的受众”并不一定意味着你需要将所有东西都降低水平。

想想大学教授教本科生。在学期开始的时候,可能需要慢慢来,在解释事物时要非常慎重。但是,随着特定术语和概念开始为全班所熟悉,自由地使用这些术语可能就更好。

同样,当有一些术语和概念很难被人们理解时,慢慢介绍这些概念而不是完全避免使用这些概念可能更有意义,这样学生们就可以学习这些概念并在将来使用它们。

我认为问题的关键是,像往常一样,这涉及到权衡问题,你需要意识到这些问题并在你的决策中加以考虑。

可视化录像

事实上,我认为类似的事情已经发生了。当处理代码库中一些自己不太熟悉的部分时,我最喜欢的一个小技巧是使用gitblame来增进自己对代码的理解。我会看到大部分代码是谁写的,在Slack上交流,然后他们会花费大约20分钟时间给我进行大致的讲解。我觉得这非常有用。那么,为什么不像这样记录一份讲解,并在文件头部以代码注释的形式链接到这份讲解呢?

图表

好吧,让我们看看其它工具,图表怎么样?我觉得图表很棒!幸运的是,它们已经被一些人采用了。特别是在架构层次,来说明不同的模块是如何连接到另外一个模块的。

然而,我感觉图表仍然没有得到充分利用。

下面是一个例子,说明如何将它用于架构级别较低的事务。对于“水容量最多的容器”问题,以下视觉参考会非常有用:

在我看来,前端代码领域是图表尤其未被充分利用的一个领域。我认为在代码旁边伴随图表很酷,这样你就可以放一张图片展示一个React组件是什么样子的。是的,你可能已经通过打开一个网页,并且使用检查工具(或者仅仅通过常识)来确定哪些代码对应哪些UI,但这样做会有点儿小别扭。也许减少这些小别扭是一个不错的主意。

特别是我想到的以下几点。你的文本编辑器中应该有这个插件。当你的文本编辑器看到一段代码注释后面跟着一个以.jpg结尾的URL

//

复制代码

我的朋友BranLong有一个好主意:使用某种插件根据这些组件的一些模拟数据自动生成这些图表或图片。

总之,这条思路不仅仅是我个人的强烈感觉,更是一种猜想,但确实很有趣!

Cleancode

就像我在文章开头所说的,如果你将自己当作一个教导团队其他人如何使用这段代码的人,很多公认的关于cleancode的想法都会自然而然地产生。描述性变量名、模块化、恰当的缩进,等等。我还从clean-code-javascript“借”了一些示例过来。

差的代码

//Whattheheckis86400000for?setTimeout(blastOff,86400000);

复制代码

好的代码

//_IN_A_DAY=60*60*24*1000;//86400000;setTimeout(blastOff,MILLISECONDS_IN_A_DAY);

复制代码

差的代码

constaddress="OneInfiniteLoop,Cupertino95014";constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;saveCityZipCode((cityZipCodeRegex)[1],(cityZipCodeRegex)[2]);

复制代码

好的代码

constaddress="OneInfiniteLoop,Cupertino95014";constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;const[_,city,zipCode]=(cityZipCodeRegex)||[];saveCityZipCode(city,zipCode);

复制代码

差的代码

functionemailClients(clients){(client={constclientRecord=(client);if(()){email(client);}});}

复制代码

好的代码

functionemailActiveClients(clients){(isActiveClient).forEach(email);}functionisActiveClient(client){constclientRecord=(client);();}

复制代码

差的代码

functionaddToDate(date,month){//}constdate=newDate();//It'shardtotellfromthefunctionnamewhatisaddedaddToDate(date,1);

复制代码

好的代码

functionaddMonthToDate(month,date){//}constdate=newDate();addMonthToDate(1,date);

复制代码

代码注释

我比较倾向于传统的日志类注释的写法

/***2016-12-20:Removedmonads,didn'tunderstandthem(RM)*2016-10-01:Improvedusingspecialmonads(JP)*2016-02-03:Removedtype-checking(LI)*2015-03-14:Addedcombinewithtype-checking(JR)*/functioncombine(a,b){returna+b;}

复制代码

位置标记

//////////////////////////////////////////////////////////////////////////////////ScopeModelInstantiation////////////////////////////////////////////////////////////////////////////////$={menu:"foo",nav:"bar"};//////////////////////////////////////////////////////////////////////////////////Actionsetup////////////////////////////////////////////////////////////////////////////////constactions=function(){//};

复制代码

明显的注释

functionhashIt(data){//Thehashlethash=0;//Lengthofstringconstlength=;//Loopthrougheverycharacterindatafor(leti=0;ilength;i++){//=(i);//Makethehashhash=(hash5)-hash+char;//Convertto32-bitintegerhash=hash;}}

复制代码

应该避免这样使用位置注释和明显的注释。然而,我对于“好代码通常是它自身的文档”这一点有所保留。我通常默认假定:

我注意到人们在工作中使用解释性注释,而我不会在那些场景中这样做,但我发现这些注释确实很有用;

这种“像教育者一样思考”的框架让我觉得它们很有价值。

我不想争论这些注释是没有被充分利用的。那很难。相反,我只是想建议你据此重新评估自己的立场。下次编写函数时,问问你自己,是否会有其他人会很难理解你所写的代码。问问你自己,是否可以添加一些不会显得多余和臃肿的注释。问问你自己,一名教育者会怎么做。

后记:像个可用性设计师一样思考?

这篇文章是关于你在写代码时像一名教育者一样思考。我认为这个想法很好,但这是唯一的好想法吗?填空:“像个____一样思考代码质量”。还有哪些有意义的想法?

我想到的最重要的是“可用性设计师”。为什么?因为我一直认为,用户测试是人们应该在代码库中做的事情!

想想看,我在文档最后一段所说的:

可用性设计师一直在做这类事情!这是他们的工作!但不仅如此,他们还做其它哪些事情?

用户测试!他们不会凭空猜想人们会理解如何使用他们的产品。他们会进行测试。把它放到真正的用户面前,看看有哪些别扭的点。为什么我们不能对代码也这样做呢?

版权声明:本站所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,不声明或保证其内容的正确性,如发现本站有涉嫌抄袭侵权/违法违规的内容。请举报,一经查实,本站将立刻删除。

相关推荐