写代码是我们程序员的立身之本,有三个问题值得好好思考下:

什么样的代码是好代码?

很炫的代码。 每个类都散发着设计模式的味道。 每个处理流程都曲径通幽,需要读者焚香沐浴,静心细品,才能发现作者的匠心独运。

这就是好代码吗?

Oh no,也许不是这样的。

首先代码能跑起来,实现用户的功能需求。然后再谈写得好不好。 怎么算好呢?

  • 简洁明了
    • 代码(函数,类)功能单一
    • 一个函数只干一件事情,一个类只抽象一个明确的主题。不要只盯着面向对象里面有一个SRP(单一职责原则),实际上,不管是面向过程,也不管是面向对象还是函数式编程,内聚是永远不会错的。又想上班,又想炒股又想做生意,结果可能是什么也做不好。又接收报文,又解析报文,又校验报文,又存库,又组装报文,又发送报文,全铺开在一个函数里面,结果这个函数就变成了脏乱差。
    • 实际上,大部分的函数,按功能封装封装,接收和解析报文提取一个函数,校验报文一个函数,组装报文一个函数,发送报文一个函数,就能弄得汤清水净,各个函数都功能单一了。

只有很少的机会,才会碰到需要我们拆分接口,或者按聚合、组合还是继承关系划分出几个类,才能实现功能单一的情况。

  • 代码干净利落,不拖泥带水 如果功能单一,那么干净利落已经做到大半。

  • 变量、函数、类命名清晰、准确

    • 这个本来是不消说的。生个娃,取个好名字能让娃今后万事如意,这是所有家长都懂的了。定义个变量,写个函数,创建一个类,写个宏,如果从名字根本看不出来是用作啥的,那读者看着多闹心。同一个文件中的函数名:有的全小写,用下划线连起来各个单词,这是linux风格的命名;有的大小写相间,用大写字母分开各个单词,这是骆驼命名法;有的又有下划线,又有大小写,好像雨天披着雨衣还同时打着伞,这是混搭命名法。总体看这是不好的命名。

    • 按编码规范,整齐统一,像给娃取名那样,谨慎的命名自己的变量、函数、类、宏的,这才是极好的。

  • 表达到位

  • 代码的意图清楚

    • 意图清楚,就是读者一看代码,就知道是干什么的。代码首先是给人看懂,其次才是给机器看。简洁明了,是意图清楚的前提。

    • 注释恰当,不多不少。 不要故弄玄虚。不要使用不必要的STL。不用使用不必要的复杂算法。可以用简单数组搞定的,就不用指针、堆内存。

    • 命名多用set、get、check这些标准单词。 更主要的,如果你非要大段注释才能说明白一段代码,这个代码多半需要重构。 你可以把一组、甚至一条语句提取为一个小函数,只为让函数名称说明这是要干啥。

  • 代码的处理逻辑层次分明

    • 写代码和写文章是一样一样的。谋篇布局,主次分明,逻辑清楚,才好看。如果你实在没啥逻辑,就给代码标上1、2、3、4吧。写文章可以有章节编号,1,2,3,4,写代码为什么不能有?
  • 代码的处理逻辑,与问题领域的概念、规则吻合

    • 这个是比较高的境界。可意会。言传就落了俗套。与问题领域吻合,意思是代码的样子,符合我们对代码所描述的事物的直观认识。非高手不能。非多次重构不能。

安全可靠

代码运行状态、时序管理得当

尽量保证函数是可重入的。尽量不用全局变量。 独占资源(锁定数据库、占用信号量)的时间尽量短。

不滥用assert。传入数据不合要求你就挂起,真的合适吗? 避免时序引起的偶发故障。别人的进程起来得比平时慢了,你就可以出错吗?

代码具有必要的测试安全网络

用例跑完一遍要快。秒级最好,分钟也行,不超过10分钟可以接受。为啥?用例跑得太慢,就没人爱用,没人爱用,修改代码就不安全了呢。

易于扩展

扩展新功能时,只需新增代码,无需改动老代码

不要只盯着面向对象里面有一个OCP(开放-封闭原则),实际上,不管是面向过程,也不管是面向对象还是函数式编程,不动老代码、也能扩展新功能,都是一种追求。 老代码都测试过了,能不动当然好啊。

容易做到吗?不容易。 但至少,你不能加一个小功能,都要翻天覆地改一遍吧?那只能说明你原来的设计太逊了。

C语言里面,函数功能单一化,在易变的地方采用注册回调机制、表驱动机制,都可以把改动限制在局部,保持老代码的稳定。 C++里面,工厂模式,让你创建新子类的对象时,不用改动使用这个对象的客户类代码;策略模式,让你面对不同产品的处理差异,不用改动基类和客户类代码,这都是一些具体办法。

代码是资产还是债务?

黄蓉每天写500行代码。郭靖每天写100行代码。 黄蓉比郭靖厉害? 都是完成相同的功能,慕容复拥有2000行代码。乔峰只拥有200行代码。 慕容复比乔峰厉害?

用户要的是代码还是功能?

代码能按行收钱吗? 古龙写武侠小说,经常是一句话一行,一行一段。据说那时候台湾小说界是按行付稿费的。 问题是代码不是小说。

代码的目的是实现用户需求。 在满足用户需求的前提下,我们的代码应该—— 尽可能少。 看看下面的代码。这样的代码,大量出没在超过10000000行的软件项目中。

维护这些代码,得花多少人力? 维护者是否会觉得上辈子欠了原作者的债呢。 公司和项目是否都在背着这些代码的债务,负重前行呢? 有些严谨又有趣的老外,好像已经把这个债务算清了: Technical Debt Is Now Costing Us $3.61 Per Line Of Code(“CAST估计,现在公司要解决技术负债的花费,是每行代码3.61美元。”). – Jonathan Bloom From CAST.

代码只是设计的实现吗?

据说程序员都是码农。 真正的大牛只画UML图,偶尔提出一些软件思想和理论,供码农们崇拜。 码农们像蚂蚁那样coding,给别人的设计来作嫁衣裳。 真的是这样吗?

软件工程的理论基础,是和传统工程学做对比,尤其是和建筑业做对比。 设计主要是画图,好比画建筑图纸;实现就是coding,好比砌砖。 真的是这样吗?

建筑师画图纸,民工砌砖。没听说哪个民工砌砖砌得好变成了建筑师,没听说建筑师以前都是砌砖的。 但软件SE都是从coding过来的,而且必须是coding很好的程序员。 为什么呢?

所以,软件工程,无法解释软件系统工程师的来源问题。 如果说——代码也是设计,程序员编程的过程,也属于设计的过程。那就好解释了—— 作为程序员,你一直在设计,设计能力提高了,你就成了软件SE。

工程活动分成设计和生产。 在软件开发中,编程完全属于设计活动。我们通常说的软件SE“设计”的过程,也是软件设计的一部分。 软件里的生产工人,就是编译器。 软件的测试和调试,也是设计的一部分,是设计的验证。

如果认可这一点,除了自豪感油然而生,是否你也应该把代码写得更好点?

上述观点,纯属作者自言自语。 作为有思想的程序员,你不必同意作者的观点。

你只需想想,对这三个问题,你从前是怎么看的、现在又怎么看,这么多年过去,自己的看法是否有变化、是否有进步,就OK了。