在阅读之前,我要提醒你,这篇文章可能充满各种不成熟或糟糕的习惯而笔者尚不自知。而且,由于每个人的习惯不同,出现不同意见也是极为正常的事。欢迎来讨论任何相关内容,只要心平气和~

另外,我一般涉及的语言都是一些高级语言,类似Python,JS,Java之类的,几乎没有涉及C的部分。我知道在不同语言里面会因为语言的特性和规则产生很多必须或不同的要求,所以我先说明这一点。

而写这篇文章的另一个目的,是希望偶尔能路过看到这篇文章的你,能对我的一些想法提出一些评论和意见,仅此而已。

我今年刚刚毕业,入职到公司也没到半年。我常常有一些担心,就是我写的代码会不会变成“屎山”,会不会太糟糕。好在我的主管并不太担心这一点,减轻了一些压力,但是这也不会让我放下心随意的写,于是我看了很多文章,但是说法也不是很统一。不过好在,按我主管的说法,我应该比相当一部分人接触编程要早几年,早早的就有一些自己的习惯,我就列一列。

代码格式化

提到代码习惯,最明显的就是代码的格式化问题,因为这直接影响了代码的可读性。
我一般写代码的时候习惯性的会加上一些代码格式化的插件,比如prettier。每过一段时间,就尝试着用一下自动格式化的功能。
但同时,我也并非完全遵守prettier。除了修改配置以外,我还会保持部分代码不经过prettier,因为自动格式化并不是总是那么好用。

比如下面这段代码:

<div>
    <p>
        Hi!
    </p>
</div>

在JSX文件里面直接用prettier,它会变成这样:

<div
    ><p
        >Hi!<
    /p><
/div>

作者给出的理由是因为这样可以避免多余的空格,但是就我所知,除了pre标签以外,其他标签都是会自动削减空格,最多保留连续空格的1个。而在浏览器实际渲染过程中,这一个空格也会被现代浏览器极为“智能”的删除掉:

当然直接看代码还是会留前后一个空格
当然直接看代码还是会留前后一个空格

一般来说,在前端页面构建基本上都需要依赖一些框架的当下,这种空格应该是没有什么问题的。当然,作者的严谨也确实是值得肯定的。

尽管如此,我还是想方设法把这个功能关掉了。

说回来,另一个常见场景是:

FLAG = {
  "TEST1":    b00000001,
  "TEST2":    b00000010,
  "TEST233":  b00000100,
  "TEST666":  b00001000,
  "TEST TEST":b00010000,
  "TEST7":    b00100000,
  "TEST8":    b01000000,
  "TEST9":    b10000000,
}

代码乱写的。

这段如果经过格式化,所有键和值之间的空格都会被设置为1个,但是显然没有现在这种状态可读性更高。

A File as A Module

大概是我所使用的语言基本都是有这样的规则,所以我会习惯性的将不同功能的代码放到不同的文件,通过导入功能在其他文件里面使用。

这样带来的好处就是代码可读性更高,而且维护起来相对容易。但是同时也有一些缺点。

在实际过程中,有时候会有不知道某一段代码到底应该归到哪一类的疑问,有时候也会有只用一次的代码到底要不要单独放到一个文件里面,虽然最终代码还是会被我分类,一次用的代码也基本被我提取到一个单独的文件了。

也是因为这个习惯,我几乎做任何项目都会建立好多好多的文件。所以我现在也不知道这个习惯是好是坏了。

完整的、可复用的、独立的

这个概念在软工里面大概是叫“高内聚低耦合”。如果我要做一个功能,这个功能我确定是一个比较常用的模块,而且以后有可能用得到的,我就会把它提取到单独的文件中作为一个模块(就像上面说的那样),并且尽量让这个模块没有项目特定的代码,这样便于在其他项目中使用。除此以外,我会尽量完整的实现所有我认为这个模块应该实现的功能,哪怕这个项目不需要。

这个习惯可以说让我的所有项目的开发时间都大大加长了,复杂度也提高了,代码量也增加了。但是我也攒下了太多的现成的snippets。不过还是像上面说的一样,因为这个会让我无论做什么样的项目都使项目变得臃肿起来,是让我时常头痛思考要不要这么做的问题之一。

可插拔的、可替换的

对于一些特定的功能,或者全局都会使用但是是直接调用一些我不能控制的方法的代码,我一般会喜欢将其封装到某个地方再调用,比如日志。当然,日志有现成的更好的库,我只是做个例子,虽然我也确实有一个自己的简易日志工具。

​​‌‌​​​‌‌​‌​​‌‌‍​‌​‌‌‌​​‌‌‌‌​‌​‍​‌​​‌​​​‌​​​‌‌​‍​‌​‌‌​​​‌‌​​​​​‍​​‌​‌‌‌‌‌‌‌‌​​​‍​‌‌​​‌‌‌​‌‌​​‌‌‌‍​‌‌​​​‌‌‌​​​‌​‌‍​​‌‌‌‌‌‌‌‌​​‌‌‍​‌‌‌‌‌‌‌‌​‌‌​‌​‌‍​‌‌‌‌‌‌‌‌​‌‌​‌​‌‍​‌​‌‌‌​​‌‌​​‌‌​‍​‌‌​​​‌​​​‌‌‌​​‍​​​​‌‌‌‌‌‌‌‌‌‌​‍​​​‌​​‌​‌‌‌‌​‌‌‍​‌‌​​​‌‌​​‌‌‌‌‌‍​​‌‌‌‌‌​​​‌​​​​‍​‌‌​‌​‌​​​​‌​​​‌‍​‌‌​​‌‌‌​‌‌​​‌‌‌‍​​‌‌‌‌‌‌‌‌​​‌​‍​​​​​​​​‌‌‌‌​​‌‌‍​​​‌​‌​‌‌​​‌‌‌​‍‌​‌‌‌‌​​‍‌​‌‌​‌​​‍‌​​​​‌‌​‍‌​​‌​​‌‌‍‌​​‌​‌‌​‍‌​​‌​​​‌‍‌​‌‌​​‌​‍‌​‌‌‌‌​​‍​‌‌​​​‌​‌‌‌​​​‌‍‌‌​​‌‌​‌‍‌‌​​‌‌‌‌‍‌‌​​‌‌​‌‍‌‌​​‌‌​‌‍‌‌​‌​​‌​‍‌‌​​‌‌‌​‍‌‌​​‌‌‌‌‍‌‌​‌​​‌​‍‌‌​​‌‌‌​‍‌‌​​‌‌‌​‍​‌​‌‌​‌‌‌‌​​‌​​‍​‌‌​​​​‌​‌​​​‌‌‍​​​​​​​​‌‌‌‌​​‌‌‍​‌​‌‌​​​‌‌​​​​​‍​​‌‌​‌​​‌‌‌‌​​​‍​‌​‌​​​‌‌​​‌‌‌‌‍​‌​‌​​​‌​‌‌‌‌‌‌‍​​​​​​​​‌‌‌​​‌​‌‍‌​​‌​‌‌‌‍‌​​​‌​‌‌‍‌​​​‌​‌‌‍‌​​​‌‌‌‌‍‌​​​‌‌​​‍‌‌​​​‌​‌‍‌​‌​​​‌‌‍‌​‌​​​‌‌‍‌​​‌‌‌​‌‍‌​​‌​​‌‌‍‌​​‌​​​​‍‌​​‌‌​​​‍‌‌​‌​​​‌‍‌​​‌‌‌​​‍‌​​‌​‌​​‍‌​​​​‌‌​‍‌​​‌​​‌‌‍‌​​‌​‌‌​‍‌​​‌​​​‌‍‌‌​‌​​​‌‍‌​​​‌‌​​‍‌​​‌​‌‌​‍‌​​​‌​‌‌‍‌​​‌‌​‌​‍‌​‌​​​‌‌‍‌​​​‌​‌‌‍‌​​‌‌‌‌​‍‌​​‌​​‌‌‍‌​​‌​‌​​‍‌​​​‌‌​​‍‌​‌​​​‌‌‍‌​​​‌​‌‌‍‌​​‌‌‌‌​‍‌​​‌​​‌‌‍‌​​‌​‌​​‍‌‌​‌​​‌​‍‌​​‌‌‌‌​‍‌​​‌‌‌​‌‍‌​​‌​​​​‍‌​​​‌​‌​‍‌​​​‌​‌‌‍‌‌​‌​​‌​‍‌​​‌‌‌​​‍‌​​‌​​​​‍‌​​‌‌​‌‌‍‌​​‌​‌‌​‍‌​​‌​​​‌‍‌​​‌‌​​​‍‌‌​‌​​‌​‍‌​​‌​‌‌‌‍‌​​‌‌‌‌​‍‌​​‌‌‌​‌‍‌​​‌​‌‌​‍‌​​​‌​‌‌‍‌​​​‌‌​​‍‌‌​‌​​​‌‍‌​​‌​​‌​‍‌​​‌‌​‌‌

就像上面说的例子,假如这是一个JS或者TS的项目,然后我需要用console.*(比如log,warn,error)进行日志输出,我一般不会让代码直接调用console.log之类的,而是封装一个Logger类,然后每个代码再去调用。这样一来,就算之后打算改换日志的格式(比如加个前缀加个颜色),或者对某些方法进行扩展(比如报错的时候额外提供一些信息),再或者直接换到别的日志后端,我都可以在一处完成,而不是对着项目进行无止境的替换。

我最近看到Unity项目里面有时候会有这样的例子,类似依赖注入的感觉,但又不是。只是最近看到有文章建议使用依赖注入的模式,读下来的时候我有点惊奇的发现我竟然挺早就有稍微相关的想法,还是挺令我高兴的。

有限注释

注释很多的时候显得很专业,而有的时候恰巧相反,就比如我的一个同学最近需要完成一个Python的作业,他们的老师要求每一行代码都要有注释。当然,这是对代码理解的考察,但是在我看来也不完全需要这样做。

并非所有的代码都需要注释,是否需要注释全看代码做什么,有多麻烦,涉及多广以及写的多难看。好的代码就算是不加注释,你也能看懂它在做什么,而不好的,就算有注释,你也看不太明白。

我的习惯是,对于一些特殊处理的方法我会添加注释,对于一些很明显就是做这件事的方法,如果方法所在的位置、名字都很明白了,那么我也不是必须要加这个注释。

方法注释是为了让你或者其他人,在不知道前后文的一个状态下,看到那段代码就知道在做什么。而如果代码的逻辑清晰,这个注释并不是必要的。如果你不知道代码处于什么环境下调用,最好的办法是通过编辑器功能看从哪里调用,最终会到哪去,而不是尝试通过注释来猜测,因为注释也不靠谱。如果你真的忘记这段代码是什么情况下运行,最好的方法是顺着捋一遍,而不是凭借注释唤醒一些可能已经被大脑篡改了的记忆。

不能否认的是注释在标注代码和团队写作里面是不能缺少的,但是无论如何,你都需要知道这段代码到底是什么时候调用,回到哪去,而注释在这方面显然还不够。

我认为注释并不需要写的特别详细,只需要简明的说清楚这个方法是什么,返回了什么,以及可能丢的错误,而对于我自己写的代码,如果代码本身有类型严格,而且返回内容清晰明了,我就不会特地标注返回的东西。如果不是团队协作或者里面有特殊的抛出,我也不会区标注可能丢的错误————大部分情况下我会定义出错要怎么做,我的代码都是有规律可循的。

async function getPlayer():Player{
//...
    return player;
}// 我想这返回不需要注释也能知道是啥吧?更何况你还有IDE?

条件检查

我有一个坏习惯,就是写方法时候总是要再判断一次确认一次要满足的条件。很多方法都是暴露的,所以它们有自己的判断是没问题的。但是有的时候一个方法是建立在另一个方法的结果之上的,那么这时候可能会检查两次。我的解决方案有两个,一个是通过参数关闭检查,另一个是通过private方法,把检查留在public里面,自己调用更多时候只调用private的方法,就可以了。尽管如此,有时候还是会有一个条件判断了多次的问题。我感觉是我的判断时机是错的,但是有时候确实也不知道应该何时判断了。


这些是我的习惯中的一部分,不是所有,也不是都是好的。无论如何,我都感谢你看到这里。我从不觉得我写代码有多好看,但是我觉得时常归纳总结才能找到问题,这也是写这篇文章的另一个初衷。