代码精进之路

开篇词

代码规范篇

1、条件运算符的使用

学习这篇文章之前,我非常喜欢使用条件运算符。因为条件运算符的这种压缩方式,使代码看起来简短、整洁、干净。 而且,如果能把代码以最少的行数、最简短的方式表达出来,心里也颇有成就感。
后来,我的一位同事告诉我,对于我使用的条件运算符的部分代码,他要仔细分析才知道这一小行代码想要表达的逻辑,甚至有时候还要翻翻书、查查操作符的优先级和运算顺序,拿笔画一画逻辑关系,才能搞清楚这一小行代码有没有疏漏。更为恐怖的是,时间久了后,我自己也不能一眼看出这一行代码是否合理了。
个人建议:不使用嵌套的条件运算符,单个简单一眼可以看出的可以用。
好的代码应该是:

  1. 容易理解
  2. 没有明显的安全问题
  3. 能够满足最关键的需求
  4. 有充分的注释
  5. 使用规范的命名
  6. 经过充分的测试

坏的代码包括:
7. 难以阅读的代码
8. 浪费大量计算机资源的代码
9. 代码风格混乱的代码
10. 复杂、不直观的代码
11. 没有经过适当测试的代码

2、阻挡错误的五道关卡

开始之前,我先给你讲个曾经发生过的真实案例。2014 年 2 月,苹果公司的 iOS 和 OS X 操作系统爆出严重的安全漏洞,聪明的黑客们可以利用这一漏洞,伪装成可信 站或者服务,来拦截用户数据。而造成这一漏洞的原因,也让业界专家大跌眼镜。

没有人是完美的,人人都会犯错误。这应该是一个共识。这里面既有技术层面的因素,也有人类的行为模式的因素,也有现实环境的影响。我们在此不讨论人类进化和心智模式这样的严肃研究成果。但是,有两三个有意思的话题,大家可以一起看看。

  1. 第一个比较普遍的观点是好的程序员不会写坏的代码,要不然,就是他还不足够优秀。我尊重这个观点背后代表的美好愿望,但是这个观点本身我很难认同。它一定程度上忽视了人类犯错误的复杂性,和影响因素的多样性
  2. 第二个更加普遍的观点是同样的错误不能犯第二次。作为一名程序员,我同样尊重这个观点背后代表的美好期望。但是,我想给这个观点加一点点限制。这个观点应该是我们对自身的期望和要求;对于他人,我们可以更宽容;对于一个团队,我们首先要思考如何提供一种机制,以减少此类错误的发生。 如果强制要求他人错不过三,现实中,我们虽然发泄了怨气,但是往往错失了工作机制提升的机会。
  3. 第三个深入人心的观点是一个人犯了错误并不可怕,怕的是不承认错误。同样的,我理解这个观点背后代表的美好诉求。这是一个深入人心的观点,具有深厚的群众基础,我万万不敢造次。在软件工程领域,我想,在犯错这件事情上,我们还是要再多一点对自己的谅解,以及对他人的宽容。错误并不可怕,你不必为此深深自责,更不应该责备他人。要不然,一旦陷入自责和指责的漩涡,很多有建设意义的事情,我们可能没有意识去做;或者即使意识到了,也没法做,做不好。

那么问题来了,人人都会犯错误,还容易重复犯,还又不能批评,这怎么能编写出优秀的程序呢个时候我们就需要该部分提及的五个关卡了

第一道关:程序员本身

程序员自我修养的提升是个永不过时的课题。从失败中学习、积累、提高,是我们成长的必修课。这也是我们一直在努力做的事情。
魔鬼藏于细节,很多时候,都是由于我们的代码风格造成的,但是我们依旧会有没注意到的问题,所以也就有了第二道工序:IDE

第二道关:IDE

在现如今,大多数编码问题都可以通过IDE直接反馈给我们,对于IDE给我们的警告,一定要消除,就算不消除,也一定要搞清楚警告产生的原因,并确认不消除它,对于我们程序的运行是不是有影响。

第三道关:回归测试

一般情况下,无论是开发人员,还是测试人员,都要写很多测试代码,来测试软件是否达到预期的要求。这些测试代码的一个关键用途就是做回归测试。只要有一个代码的改动,我们可以用回归测试来检查这个变动有没有对以前的代码造成问题。
软件测试没有办法覆盖所有的使用场景。但是,我们千万要覆盖关键逻辑和负面清单。一个没有良好回归测试的软件,很难保证代码变更的质量;也会使得代码变更充满不确定性,从而大幅地提高代码维护的成本。

第四道关:代码review

代码review,不需要有特别厉害的大牛。添加的这行代码,还是相当刺眼的。多一些眼睛盯着这些代码,多一些形式展现这些变更,就会大幅度地降低问题藏匿的几率。

第五道关:代码分析

代码覆盖率(Code Coverage)是一个反映测试覆盖程度的指标。它不仅仅量化测试的指标,也是一个检测代码缺陷的好工具。如果你的代码覆盖率测试实现了行覆盖(Line Coverage),这个“GoTo Fail”问题也很难闯过这一关。

3.优秀程序员的六个关键特质

掌握一门编程语言

我们都会使用筷子,吃饭的时候,我们不需要有意识地控制着筷子的力度、开合和角度,也能准确地使用它。这个使用筷子的效率,是我们小时候长期练习的结果。每个人拿筷子的方法可能会有些差异,但是都不影响我们现在精准地、高效地使用筷子。
编写程序也是这样。熟练了之后,很多语法、语句在我们编写程序的时候,会下意识地就流露出来。
编程语言,基本上是相通的。掌握了第一门编程语言后,第二门语言学起来就快很多,第三门语言学起来更快。现在我们几乎都是多语言使用者,但一定要先精通一门语言,达到像用筷子那样的熟练程度。

解决现实的问题

有了工具还不够,优秀的程序员还要深入理解问题,懂得问题的最核心价值。只有理解了问题,看到了解决问题的价值,我们才能够真正解决好问题,并且从中获得满满的成就感。我们一定要记得,程序员的存在不是为了写代码,而是为了解决现实问题,实现现实价值。
优秀的程序员,是一个内外双修的程序员。如果一个程序员可以熟练使用工具,有清晰的解决问题思路,能明晰地传达产品价值,那么他编写代码就不存在什么巨大的困难了。

发现关键的问题

有了工具,遇到问题能解决掉,我们就可以做事情了。优秀的程序员还有一项好本领,就是发现关键的问题。能够发现关键的问题,我觉得是一个好程序员和优秀程序员的分水岭。
能够发现关键的问题,意味着我们可以从一个被动的做事情的程序员,升级为一个主动找事情的程序员。
能够发现关键的问题,往往需要我们对一个领域有很深入的研究和深厚的积累,并且对新鲜事物保持充分的好奇心和求知欲。
掌握一门编程语言,解决现实的问题,能发现关键的问题,做到这三点,你就已经是一名优秀的程序员了。如果说优秀程序员有一个评价标准的话,这三条一定是硬性指标,接下来再介绍三条软性指标。

沉静的前行者

首先,优秀的程序员,一定是懂得妥协,懂得选择,一步一步把事情沉静地朝前推动的人。
如果真的较起真来,每一行代码,就像孔乙己的茴香豆,都有不止四样的写法。可是,最终的程序,只能选择唯一的一种。优秀的程序员都有在不断平衡、不断妥协中推动事物前行的能力和修为。如果一个人说要一个完美的代码、完美的算法,完美的程序、完美的产品,我立刻就会非常紧张。完美是不存在的,所以我们才追求完美。对完美的过分追求,可能是一个代价高昂,收获甚小的行为。很多时候,我们不需要完美的东西。如果我只是想看看泰山山顶的日出,你就不要问我是爬上去的还是乘索道上去的了。

我们写的每一行代码,都可能存在问题。有时候,我发现别人的代码的问题;有时候,别人发现我的代码的问题。我们最后都会明白,要坦诚地面对别人的问题,也要坦然地面对自己的问题。在解决问题和帮助别人解决问题中,我们把一个产品变得越来越好,问题越来越少。

值得信赖的伙伴

如果我们把软件开发看成一个循环的流水线,参与其中的每个人,都要接受来自上一级的输入内容,在当前环节和同事合作,创造面向下一级的输出内容。优秀的程序员,知道团队合作的重要性,是一个优秀的团队成员。他在团队中能够快速学习、成长,变得越来越优秀,也能够帮助其他团队成员变得越来越优秀。

优秀的程序员是一个领导型的人。他能够倾听,持续地获取他人的优秀想法,以及不同的意见。他能够表达,准确地传递自己的想法,恰当地陈述自己的意见。他是一个给予者,给别人尊重,给别人启发,给别人指导,给别人施展才华的空间。他是一个索取者,需要获得尊重,需要获得支持,需要持续学习,需要一个自主决策的空间。他能够应对压力,承担责任,积极主动,大部分时候保持克制和冷静,偶尔也会表达愤怒。他具有一定的影响力,以及良好的人际关系,能够和各种类型的人相处,能够引发反对意见,但是又不损害人际关系。他知道什么时候可以妥协,什么时候应该坚持。

上面的这些,通常称为“软技能”。如果说,编程语言、花样工具、逻辑思维、解决问题这些“硬技能”可以决定我们的起点的话,影响力、人际关系这些“软技能”通常影响着我们可以到达的高度。因为,无论我们是加入他人的团队,或者组建自己的团队,我们只有在团队中才能变得越来越出色,做的事情越来越重要。所以,我们需要成为优秀的团队成员,接受影响,也影响他人。

时间管理者

时间面前,人人平等,没有人一天的时间比别人多一秒。优秀的程序员会更好地管理时间,或者提高效率,或者用好时间。

要做只有你才能做的事情。是的,有很多事情,只有你可以做,只有你做得最快最好。其他的同事也是一样的,有很多事情,只有他们能做,只有他们做得最快最好。选择最合适的人做最合适的事,这不仅是领导的工作分配,也可以是我们自己的协商选择。

事情做不完,就需要面临选择。要坚持做需要做的事情。不需要的、不紧急的、价值不大的,我们可以暂时搁置起来。一个人,能做的事情是有限的,能把最重要的事情最好,就已经很了不起了。

4、代码命名问题

规范代码命名的好处:

  1. 为标识符提供附加的信息,赋予标识符现实意义。帮助我们理顺编码的逻辑,减少阅读和理解代码的工作量;
  2. 提高代码的清晰度、可读性以及美观程度;
  3. 避免不同产品之间的命名冲突。

5、代码整理

代码分好块。如果一段代码放眼望去都是大块大块的,那对于一个初学者来说,阅读和理解就非常困难。他需要将复杂的表达式再次分解,分解到可以单独理解的变量和运算符,再重新组合。

  1. 保持代码块的单一性,一个代码块只能有一个目标。代码块内所有的内容都是为了一个目标服务的,不能把无关的内容放在同一个代码块里。同一个代码块里语句的相互联系比与相邻代码块里的语句关系更为紧密;
  2. 注意代码块的完整性。代码块是一个完整的信息块。一个代码块要表达一个相对完整的意思,不能一个意思没说完就分块了,就像话说了半句一样;
  3. 代码块数量要适当。代码块过多,会让人觉得路径太长,逻辑复杂,不容易阅读理解。一个基础的代码块最好不要超过 25 行(通常显示屏小半个页面),否则就会有增加阅读理解的困难。

6、写好注释

在理想状况下,代码不需要注释。理想的代码,命名恰当,结构清晰,逻辑顺畅,含义显而易见。但正如一个作家无法预料他的读者能否清晰地理解他的文字一样,一个程序员也不能判断他的读者能否清晰地理解他写的代码。所以,写注释其实是下巧功夫。
注释带来的三个麻烦:

  1. 因为注释不需要运行,所以没有常规的办法来测试它。 注释对不对没有随着代码变更些问题都是写注释需要注意的地方。注释难以维护,这是使用注释带来的最大的麻烦
  2. 注释为我们提供了一个借口。使用注释来解释代码,是注释的本意。但是,我们有时候会过度依赖解释,从而放弃了潜在的替代方案,比如更准确的命名,更清晰的结构,更顺畅的逻辑等等。 注释,被我们用成万能的狗皮膏药,有时会让代码更糟糕
  3. 注释的滥用。 由于注释部分不被执行,那么就可以被用来注释掉一些不需要的东西。比如,在正式的产品代码中,注释掉调试信息、代码块、俏皮话等等。

注释要遵守的三点:

  1. 准确,错误的注释比没有注释更糟糕。
  2. 必要,多余的注释浪费阅读者的时间。
  3. 清晰,混乱的注释会把代码搞得更乱。

一般面向国内的需求文档时,我们还是建议用中文,面向国际的用英文注释。

7、组织好代码文件,要有用户思维

最开始接触一个项目代码时,我们最渴望的,就是快速揭开项目的面纱。这个项目是干什么的怎么做的怎么使用br> 有很多这样的问题,排着队等我们处理。我们需要从一个点开始,先撕破一点点皮,然后,像剥洋葱一样,一层一层地阅读,一层一层地了解。
一般地,一个典型的软件项目,拥有上百个文件是寻常的事情。如果这些文件组织混乱,就会让整个项目乱糟糟的,我们很难入手去查找、阅读和测试。
文件的组织要层次分明、易于检索、一目了然。要做到这一点,我们可以从用户思考问题的逻辑入手。

逻辑之一:软件是干什么的/h5>

一个软件项目开始时,这个问题的答案可以不是很丰满,但是,最基本的思想一定要有。随着软件的研发进程,它的描述可以越来越清晰。软件成型之前,这个问题必须干脆地解决掉,得到明确的答案。

逻辑之二:软件可以拿来用吗/h5>

所有的软件,都有归属,都受版权的保护。谁拥有这个软件的版权是我们需要关注的一个问题。
当使用软件的时候,不能超越许可证约定的范围。 一个没有许可证的软件,我们是不能使用的,因为不知道许可的范围,也不知道应承担的义务。同样,如果一个软件的许可证不清晰,或者我们不了解,那么使用起来也会有很多法律问题。

逻辑之三:软件该怎么使用/h5>

一个好的软件,要尽可能降低使用门槛。编写使用指南和代码示例是两个常用的办法。一份好的用户文档,应该让软件的用户快速入门,然后再逐步深入地了解整个软件的使用细节,以及潜在的问题。

作为程序员,我们不仅要熟悉源代码,还要熟悉文档。当需要更直观的用户指南或者代码示例时,就要写作这样的软件文档。对于每一行的代码变更,我们都要问,需不需要文档变更果代码和文档一致的话,就会节省我们大量的维护时间和维护成本。

接口文档

一般项目,只是面向开发人员开发时所用的文档;有的项目可能需要面向用户,这里我们就不赘述了,有兴趣可以自己去看看(怎么写好用户指南

接口是调用者和实现者之间的合约,所以对它就有了更加严格的要求,主要有四个原则:成文、清楚、稳定、变更要谨慎。

  1. 合约要成文:无论对于调用者,还是实现者来说,外部接口的使用都要有章可循,有规可依。如果调用者需要去看实现代码来理解外部接口,那么外部接口和内部实现的分离还有什么用呢就背离了外部接口和内部实现分离的初衷吗样做既是对实现者的纵容,也是对调用者的无视。
  2. 合约要清楚:合约既然是我们协作的依靠,就一定要清晰可靠、容易遵循,不能有模棱两可的地方。如果接口规范描述不清,既误导调用者,也误导实现者。
  3. 合约要稳定:接口的设计和规范的制定,一定要谨慎再谨慎,小心再小心,反复推敲,反复精简。一旦接口合约制定,公布,然后投入使用,就尽最大努力保持它的稳定,即使这个接口或者合约存在很多不足。
  4. 变更要谨慎:接口合约毕竟不是租房合约,可以一年一续,每年变更一次。租房合约的变更成本很小,但软件的接口合约变更的影响要严重得多。特别是兼容性问题,稍微一丁点儿的接口规范变化,都可能导致大面积的应用崩溃。越成功的接口,使用者越多,变更的影响也就越大,变更的成本也就变高,变更也就越困难。所以,对于接口规范,我们的原则是,能不变更就不变更;必须的变更,一定要反复思量该怎么做才能把影响降到最低。

代码经济篇

1、为什么需要经济的代码

根据有关 道,2014 年 1 月 9 日,火车票售票 站点击量高达 144 亿次,相当于每个中国人点击了 10 次,平均每秒点击了 16,000 次,峰值的点击量可能远远超出 16,000 次。这么强悍的访问量,导致了火车售票 站多次瘫痪。这是一个典型的性能错配导致的重大 络事故,处理这么大的点击量需要特殊的程序设计和架构安排。

需不需要学习性能

一个程序员,可以从多个方面做出贡献。有人熟悉业务逻辑,有人熟悉类库接口,有人能够设计出色的用户界面。这都非常好,但是如果考察编程能力,有两件事情我们需要特别关注。

  1. 第一件事情是,我们的代码是不是正确实上,代码正确这个门槛特别低。如果代码出现了大范围的错误,说明编程还没有入门。
  2. 第二件事情是,我们的代码运行起来有没有效率,运营成本低不低也是我们判断代码是否经济的一个标准。编写经济的代码的门槛稍微高一些,它需要更多的知识和经验,但它也是能让我们脱颖而出的一个基本功。门槛越高,跨越门槛的价值就越大。我们要是一直不愿意跨越这个高门槛,面临的竞争压力就会越来越大。

一个好的程序员,他编写的代码一定兼顾正确和效率的。事实上,只有兼顾正确和效率,编程才有挑战性,实现起来才有成就感。如果丢弃其中一个指标,那么大多数任务都是小菜一碟。

什么时候开始考虑性能问题

为了进度,很多人的选择是不考虑什么性能问题,能跑就行,先跑起来再说;先把代码摞起来,再考虑性能优化;先把业务推出去,再考虑跑得快不快的问题。可是,如果真的不考虑性能,一旦出了问题,系统崩溃,那就悔之晚矣了。

硬件扩展能解决性能问题吗

但遗憾的是,扩展硬件并不是总能够线性地提高系统的性能。出现性能问题,投入更多的设备,只是提高软件性能的一个特殊方法。而且,这不是一个廉价的方法。过去的经验告诉我们,提高一倍的性能,硬件投入成本高达四五倍;如果需要提高四五倍的性能,可能投入二三十倍的硬件也达不到预期的效果。硬件和性能的非线性关系,反而让代码的性能优化更有价值。

性能问题能滞后处理吗

越来越多的团队开始使用敏捷开发模式,要求拥抱变化,快速迭代。很多人把这个作为一个借口:我们下一次迭代的时候,再讨论性能问题。他们忘了敏捷开发最重要的一个原则,就是高质量地工作。没有高质量的工作作为基础,敏捷开发模式就会越走越艰难,越走越不敏捷,越走成本越高。而性能问题,是最重要的质量指标之一。

当应用程序进入质量保证环节的时候,为时已晚。在前面的设计和开发阶段中,我们投入了大量时间和精力。业务也要求我们尽快把应用程序推向市场。如果等到最后一分钟,才能找到一个严重的性能问题,推迟产品的上市时间,错失市场良机,那么这个性能问题解决的成本是数量级的。没有一个企业喜欢事情需要做两遍才能做到正确的团队,所以我们需要在第一时间做到正确。

2、什么是高效的代码

用户的真实感受

用户对于软件性能的要求,和我们超市结账时的要求差不多:等待时间要短,出错的概率要小。

等待时间要短

  1. 满意: 如果任务的响应时间小于 T,用户感觉不到明显的阻碍,就会比较满意;
  2. 容忍: 如果任务的响应时间大于 T,但是小于 F,用户能感觉到性能障碍,但是能够忍受,愿意等待任务的完成;
  3. 挫败: 如果任务的响应时间大于 F 或者失败,用户就不会接受这样的等待。挫败感会导致用户放弃该任务。

在互联 领域,最佳等待时间(T)和最大可容忍等待时间(F)的选择有着非常经典的经验值,那就是最佳等待时间是 2 秒以内,最大可容忍等待时间是最佳等待时间的 4 倍,也就是 8 秒以内。

体验要一致
一个服务,如果 10 次访问有 2 次不满意,用户就很难对这个服务有一个很高的评价。10 次访问有 2 次不满意,是不是说明用户可以给这个服务打 80 分呢然不是的。他们的真实感受更可能是,这个服务不及格。特别是如果有对比的话,他们甚至会觉得这样的服务真是垃圾。

代码的资源消耗

管理好计算机资源主要包括两个方面,一个方面是把有限的资源使用得更有效率,另一个方面是能够使用好更多的资源。

3、避免过度设计

软件开发过程中,最让人痛苦的是什么果有这么一个调查的话,“频繁的需求变更”应该是一个高票选项。

频繁的需求变更确实让人抓狂。它变更的可不仅仅只是需求,还有不断重构的代码,不断延长的工期,不断增长的投入,以及越来越多的加班。

要从小做起,最重要的就是选择。什么是必须做的么是现在就要做的是我们做选择时,要时刻准备提出和回答的两个问题。

首先就必须满足的需求,是优先级最高的、最重要的事情,这些事情要小而精致,是我们的时间、金钱、智力投入效率最高的地方,也是回 最丰厚的地方。我们要把这些事情做到让竞争对手望尘莫及的地步。

这点,微信就做的很好。微信的聊天页面是我们最关心的信息:谁发送了信息。一对一的聊天界面,永远只使用窄窄的一行,来完成丰富的功能,红包、语音、表情包、贴图,都可以在这一行完成。所有的其他功能,比如小程序,朋友圈、合作商家,都不能干扰核心功能的呈现。现在我们看着可能觉得很简单,其实这样的设计真的很难,真的很了不起。

4、怎么设计一个简单直观的接口

从问题出发

一个解决方案,是从需要解决的现实问题开始的。要解决的问题,可以是用户需求,也可以是现实用例。面对要解决的问题,我们要把大问题分解成小问题,把小问题分解成更小的问题,直到呈现在我们眼前的是公认的事实或者是可以轻易验证的问题。

从真实问题开始,把大问题逐层分解为“相互独立,完全穷尽”的小问题;

一个接口一件事情

我们写的每个接口都要有针对性,一行代码只做一件事情,一块代码只做一件事情,一个接口也要改只做一件事情。

如果一行代码一件事,那么一块代码有七八行,不是也应该做七八件事情吗么能说是一件事情呢里我们说的“事情”,其实是在某一个层级上的一个职责。授权用户访问是一件完整、独立的事情;判断一个用户是否已注册也是一件完整、独立的事情。只是这两件事情处于不同的逻辑级别。也就是说,一件事情,也可以分几步完成,每一步也可以是更小的事情。有了逻辑级别,我们才能分解问题,接口之间才能建立联系。

5、尽量少写代码

不重新发明轮子

“不要重新发明轮子”,这是一个流传甚广的关于软件复用的话。如果已经有了一个轮子,可以拿来复用,就不用再重新发明一个新轮子了。很多时候我们处理起来很麻烦的东西,都不防去找找 上是否有现成的轮子我们可以直接拿过来用。

一般来说,当我们使用类似的代码超过两次时,就应该考虑这代码是不是可以复用了。

推动轮子的改进

轮子发明出来了,并不意味着这个轮子就永远没有问题了。它是需要持续改进的,比如,修改错误,修复安全问题,提高计算性能等等。

“不要重新发明轮子”这句话的另外一层意思,就是改进现有的轮子。如果发现轮子有问题,不要首先试图去重新发明一个相同的轮子,而是去改进它。

编写经济代码的检查清单

做任何事的时候,我们都可以养成列清单的形式。比如需求评审的时候,我们可以考虑需求是真实的客户需求吗,要解决的问题真实存在吗,这个需求能不能分解、简化…设计的时候可以考虑能不能用现有的接口,接口之间的调用是否方便、健壮,有没有必要加缓存…

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年9月8日
下一篇 2019年9月8日

相关推荐