如何写代码 —— 编程内功心法

举报
开发者学堂小助 发表于 2017/11/15 11:42:13 2017/11/15
【摘要】 编程需要关注的问题太多,不仅仅有语言,还有算法、数据结构、编程技巧、编码风格、设计、架构、工程化、开发工具、团队协作等方方面面,涉及到很多层面的问题。本文将分享一下根据我这几年来的编程经验总结出的一些关于如何写代码的个人见解。

  写代码就是学一门语言然后开始撸代码吗?编程是一项非常严谨的工作!虽然我们自嘲为码农,但是这工作毕竟不是真正的搬砖,我们是软件工程师。编程需要关注的问题太多,不仅仅有语言,还有算法、数据结构、编程技巧、编码风格、设计、架构、工程化、开发工具、团队协作等方方面面,涉及到很多层面的问题。本文将分享一下根据我这几年来的编程经验总结出的一些关于如何写代码的个人见解。


  由于“跟我混”的一些小伙伴编程功底相对来说比较薄弱,所以在此总结一篇“编程内功心法”帮助他们渡过职业生涯的第一个瓶颈期。顺便,也造福一下路过的有缘的同学!于是有了此文。


  前言


  首先,思考一个问题,何谓编程?编程就是写代码吗?


  所谓的编程,其实就是不断的对这个现实世界中的问题建立模型并将其固化为代码自动化执行的过程。


  在对问题建立模型的过程中,我们会遇到非常多不同层面的问题,所以我们需要很多领域的知识去解决这些问题。


  我们需要管理*** 作的数据,因为数据与数据之前是相互有关联的。将数据结构化,通常是编程的第一步。关于结构化数据的相关理论以及实践,需要有一个专门的学科分支或者说课题去研究——数据结构。


  我们需要解决一个具体的问题,这个具体的问题如何一步步去解决,过程是怎么样子的——算法。


  我们需要将解决方案进行自动化,并以代码的形式进行交付——编程语言。


  如果将一个抽象的模型进行编码实现,如何实现“这个功能”,如何实现“那个功能”——编程技巧。


  问题的规模大了,众多代码糅合在一起,连程序员自己都看不懂了!怎么来拆分、模块化这些代码——设计。


  代码量已经到了一个人无法完成的地步了,需要团队分工合作才能完成了——工程化。


  你写的代码我看不懂,没法调用或者很难调用,我写的代码你也看不懂,或者很难看懂。还怎么愉快的玩耍——编码风格/编码规范。


  问题的规模继续扩大,到了系统工程的规模了,之前学的套路已经不管用了!怎么来构建这个系统才能实现正确、安全、高性能、高可用——架构。


  然而这些也只是一个系统工程中的冰山一角!这是一个庞大的体系。也正是因为软件开发需要考虑到的问题太多且团队成员水品参差不齐,所以团队开发中并不是每个程序员做的事情都是一样的。每个人都有自己的角色、初级工程师、中级工程师、高级工程师、架构师、CTO。。。


  所以编程不仅仅只是堆砌代码!


  说到这里,我想起来了一件事情——为啥业界普遍鄙视培训出来半道出家的新人?人与人的区别是很大的!我见过培训出来也很牛的。其实,说到底,被鄙视的并不是所有人。而是那些培训了几个月之后发现随便找个工作也能拿“高薪”然后还自认为编程很简单的新人。因为这种经历给了他们一种错觉——编程如此简单,我培训几个月也会嘛!有点像刚学会开车的新司机,很嚣张的对老司机说“开车很简单嘛!你看我也会啊!”。语言和开发工具只是招式,这是外功。而编程思想、经验是内功。这些内功并不是靠短短几个月的培训能够掌握的,这一点有点像中国制造业和日本制造业的区别。动不动赶英*** 可不好。。。


  编程并不简单!这是一件很严肃的事情。不过今天,我没有办法介绍完所有的方面!或者说,到今天为止,我也并没能掌握所有领域的知识。所以今天我只是分享一些关于编码本身的一些经验。


  另外,本文主要分享如何写代码,并不是如何用Java写代码。所以文章中各种语言都有可能出现。

  

编码风格


  先来一个圈内的段子。大部分程序员在工作中都很讨厌这四件事情:写注释、写文档、别人不写注释、别人不写文档...中枪了没!


  这个段子其实反映出来一个问题,即大部分代码都需要通过大量注释和文档来说明才能将意图传达给维护这些代码的程序员!然而,就像上面的段子说的那样,写大量的注释和文档其实是一件很麻烦的事情。所以很多时候,由于嫌麻烦,注释和文档就没写,导致维护代码的人相当的痛苦。这个苦同学们肯定都是体会过的!相当于给你个精密仪器要你维护还不给说明书。


  其实,打破上面那个段子描述的那个怪圈的一个很有效的手段就是统一编码风格。优秀的代码可以实现代码即注释,代码本身就可以非常清晰的体现出它的意图来,让别人可以很容易读懂。这就是所谓的可读性!

  

命名


  计算机科学领域中最难的两件事是命名和缓存失效!命名并不简单,很复杂。好的名字可以见名知意,非常容易理解。之所以说命名难是因为命名的过程同时也是概念提取的过程!对问题建立模型,需要提取概念并赋予其“术语”。这个过程其实是“万里长征”中最难的一步。毕竟,设计也好,架构也罢,都有成熟的套路可以参考。唯独这个过程,是需要程序设计者自己进行充分的思考的创造性工作!


  以下是总结出来的一些命名经验:


  一个类是某物、某事、某人的抽象,是数据与行为的集合体。这恰好符合名词的定义,因此 类名 是一个名词!


  方法名 或者说 函数名 是某操作或者某过程的抽象,是一个动作。这恰好符合动词的定义,因此函数名通常是一个动词。


  变量名宁可长一些说明清楚用途也不要用a、b、c之类的无意义的名称,除非是循环计数器中用i、j、k等约定俗成的一些变量名。比如pageIndex和pageSize就要比取名成i和s好!取成这种和用混淆器混淆过后的代码一样的名称没有什么好处,如果算法比较复杂的话,过一段时间恐怕自己都会看不懂。


  变量名最好包含变量本身的业务含义。比如ListstudentList = new ArrayList<>();就比Listlist = new ArrayList<>();好很多。如果同一段代码里再出现一个List的话,这样就可以很方便的取名为teacherList或者teachers而不是list1和list2这样的毫无意义的名称!

  

英文不好怎么办


  这个问题怎么说呢。。


  作为一名程序员吧,基础的英文还是要懂的。要不然发展也容易遇到天花板,学不好编程的。毕竟,最新的技术、解决方案、工具都是从国外传过来的。如果是解决一些基础性的问题,每天只做做CRUD,好像英文确实不怎么用得上。但是一旦遇到一些实质性问题,恐怕只能到英文网站上找喽!不要跟我说你编程可以不需要Stack Overflow。copying and pasting from stackoverflow 可是终极编程***!这句话可是编程的真谛啊!(如果你看不懂这个梗那你有可能是伪程序员)


  其实,话说回来,实在不方便用英文的时候,我认为也可以用拼音命名。这个问题上可以务实一点,量力而行。但是,拼音和英语混用的做法就不太好了。最好别这样!逼格不高。

       

注释


  关于注释,我们需要解决的第一个问题是如何添加代码注释。


  对于Java、C#之类的语言,有专用的文档注释语法,很好处理。对于C/C++,则按约定的格式说明一下类和函数、代码片段的作用和意图即可,至少编译器会进行静态检查。在Python中,有更牛逼的文档字符串这样的语言级特性支持,看注释用help()很方便。不过对于Lua这样的弱类型解释型语言,注释就比较难处理了。这里以Lua为例给出一种注释的解决方案。


  借用Java语言文档注释的风格。


  文件注释,或者说类/模块注释。

  

参考规范


  关于编码风格的问题,本文只说命名和注释这两个方面。关于缩进、空格、断行、空行等其他方面的问题,可以参考本节给出的参考规范。


  不同的企业会有不同的编码规范,所以这里没有办法给出一个符合所有公司的规范。不过制定自己团队的规范的时候,可以参考一些大企业的做法。以下是世界上最大的互联网公司谷歌的编码规范,同学们可以参考这个。


  Google Java Style Guide

  Google C++ Style Guide

  Google Python Style Guide

  Google HTML/CSS Style Guide

  Google JavaScript Style Guide

  

异常处理


  异常与返回值有什么不同


  在C语言中,我们的函数通常会返回一个整型值作为状态码用于通知客户端调用的结果。比如0表示成功,非0表示失败。并且可以通过不同的数值来表示不同原因导致的失败。然而在Java、C#、C++一类面向对象语言中,一般不会用返回值来表示状态。返回值一般用于表示返回的业务值,而异常用于通知客户端程序运行状态改变了。


  什么时候需要抛出异常


  关于这个问题,我想到了一句极其精炼的话:当函数无法完成其宣称的任务的时候抛出异常!


  比如上面的那个日子,当listArticle方法由于种种原因无法查询出文章列表的时候,则抛出异常。


  抛出异常在这种场景下是非常有必要的,因为这样其他人调用你的代码时可以非常放心的去调用,只要调用了你的方法,就会返回文章列表。如果无法返回文章列表,则会抛出异常。完全不用在调用这个函数的时候去怀疑是否执行成功了。


  再来一句至理名言:宁愿终止程序也不要带错运行下去。也就是说,遇到错误的时候,宁愿抛出异常终止程序,也不要带着错运行下去。这是在掩耳盗铃!

  

参数校验的作用


  如果在对外公开的重要方法开始的位置不插入校验参数的代码,有时恐怕方法需要运行到方法内部比较深的位置才会抛出一个异常来。而且那种情况下,抛出的异常可能就会有各种各样的了。比如空指针、除零异常等。


  这种情况下,很难一眼看出引发这个异常的根源是参数传错了。需要对你的代码进行一番调试才行!如果一开始就在代码的入口插入了校验参数的代码,那么调用的时候,一眼就能看出来是参数传错了导致了一个异常。这样其他程序员看到这个异常之后就会去看一下你的方法注释。他一看,哦!原来分页索引号是从1开始计数的,那么这个问题就会就此打住,给团队节省了时间。


  参数校验问题是会影响团队运行效率的一个很关键的因素。所以,请同学们重视起这个问题来。我们都是工程师,团队作战的,自己写代码快不叫快,整个团队快起来才叫真的快!用好断言,可以让你的代码更健壮。


  tips: Java中默认断言是不开启的,所以建议无视Java语言的断言,自己处理。


  什么时候需要进行参数校验


  我认为一个方法或者函数在满足以下条件时有必要进行参数校验:方法或者函数是对外公开的,不是私有的。参数有可能为空指针的时候。参数的合理值无法通过方法名、参数名、参数类型一眼看出来的时候!比如上面那个pageIndex是从1开始计数的,但别人并不知道你是从1开始计数的。


  如果对每一方法都进行校验的话,其实挺麻烦的。程序员的时间是很宝贵的,没这么多闲工夫。不过在满足上面条件的情况下,最好还是校验一下。因为做了这个校验,你自己是会稍微浪费几分钟的时间,不过从团队整体来看,总的调试损耗的时间却降下来了。要记住方法/函数写出来就是给别人调用的!


  参数校验需要做到什么程度


  我有一个标准,就是把自己当成调用这些代码的那个人,把自己想象成有可能以任何“姿势”调用的菜鸟(实际上也有可能是不了解你的代码的大牛)。如果这个时候自己也有可能会犯某些错(比如没注意边界值,没注意是否可空),那么这个时候是必须要做校验的。对于一些已经在其他层做过处理不太可能有错误的值的情况,可以不做校验。比如你的UserService中有一个签名为public void register(User user)的方法,用于注册一个用户。这种情况下,可以只校验一下user参数是否为空,而不用对user的username、password属性进行校验(用户名密码长度是否合法等)。因为你在上一层控制器层模型绑定的时候已经做过非常严谨的校验了。当然,这里如果你有充足的时间,也可以校验一下。具体做到什么程度,还需要你根据情况去自己把握。

  

后记


  编码规范就是用来约束别人的!哈哈!开玩笑的啦!其实很多时候,出于各种原因,如“项目周期紧”、“项目还在探索阶段可行性未知,先实现了再说”、“项目中其他代码已经这样了,破罐子破摔”等,最终导致的结果可能就是我们这些自称“有经验”的程序员自己也不一定能写出完全符合这些理念的代码来。或许是吧!

  

  我承认,我也写过奇葩代码。但是,这好像并不是你这个作为未来优秀程序员的人不思进取的理由。小时候,老师教我们要诚实,但是老师自己也不见得能完全做到。我们可以因为这个鄙视他。长大后,体验过了生活中会有很多的无奈,不再鄙视“不诚实”的老师。甚至低下了高贵的头,自己也变得那般模样。未来,你还会教育你的后代“要诚实”吗?恐怕会!因为,优秀的理念,不管结局如何,都应该去提倡!


  本文的观点仅代表现在的我,人是会成长的,明天的我或许又会有新的见解! 如果你不认同部分观点或者还有其他的优秀理念,可以给我留言。我们一起成长!


文章作者:bug辉

文章来源:https://www.bughui.com/2017/08/21/how-to-write-code/

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。