如何编写干净优雅的代码
变量名
- 可以采用 CamelCase 命名法,C# 中采用大驼峰,而 Java 采用小驼峰命名法
- 对于一些私有变量,一般的有两种方式,一种是加下划线前缀,目前我所看到的这种命名方法占绝大多数;还有一种是 m 开头的命名方法,如 m_firstName,m 代指成员变量,这在以前是很常见的,包括在微软的源码当中还能见到这样的命名方法。这两种命名方式我觉得都可以,一旦决定用那种方式,就要在开发过程中只要始终贯彻下来。
类
- 命名建议名词,名词短语或形容词,不能是动词
- 一定要采用大驼峰命名法
方法
- 命名建议动词,动词+短语,能直接表明其意图的方法名:如
GetName(),FindFirstName()
- 方法表示一种疑问,bool 类型的,那么就要在最前面加 “Is” 来表示存在两种可能性,如
IsValid(), IsEnum(), IsSuccess()
- 一定不要为了一时偷懒,给方法名增加简称,如
GetProvince()
简写成 “GetP”,这样很难让其他阅读你代码的程序员第一眼能得知你的意思,在我们项目当中,有这样的代码:public static DateTime GetDefaultReqDT()
从命名上我们无法第一时间获知这个方法的意图,这是获取默认的什么,ReqDT
指的是什么?要求时间还是请求时间?如果改成public static DateTime GetDefaultRequireTime
,我们就能很直接的知道这是获取默认的要求时间。关于简称,除非是业界公认的简称,如GetIP()
,在计算机术语中,我们就能知道 IP 的意思,写全反而显得多余GetInternetProtocolAddress()
- 方法尽量要做短小精悍,一个方法就只代表做一件事,方法里面代码行数最好不要超过 20 行(注意,是最好,不是一定)。当一个方法代码超过一屏的时候,一定要想起来重构,进行拆解多个子方法。
关于链式编程
在 C# 中,很多时候链式编程带给我很好的编程体验,它能提高阅读性和代码优雅度。但用得不好,也是很糟糕的。比如看下面代码:
list.Where(p => p % 2 == 0).Select(p => Math.Sqrt(p)).Where(p => p > 1000).ToList();
这个例子不算极端,并且是很常见的。这种写法的问题在于干的事情太多了,都放在一起了,以至于其他程序猿无法直观的看出这个方法的具体目的。还有更复杂的,比如在 Select
投影时各种判断,更有甚者,还存在嵌套 Linq的,如 list.Select(p => new { x = p.x % 2 == 0 ? p.x : 0, y = p.y % 2 == 1 , z = p.subList}).Where(p => p.x > 1000).Where(p => p.subList.Where(...).Select(...))
。这样会让其他程序猿发疯的,也许等我自己过一段时间后回过头来再看,估计同样也会发疯。像碰到这种情况,我们应该抛弃链式编程的 “一链到底” 的想法,而是拆分成多个 Linq,并且每个方法之间要换行:
list.Where(p => p % 2 == 0)
.Select(p => Math.Sqrt(p))
.Where(p => p > 1000)
.ToList();
这样就显得清晰很多了。
注释
我相信不少人关于注释有听过这么一句话
最好的注释就是不要注释
最佳实践
再写代码的时候,我们最好自上而下编写代码。《Clean Code》推荐我们在编写代码时要尽量短小精悍,这是前提。
在这个前提下,我们碰上复杂的逻辑,必然会面临拆分代码的情况,这是我们要按顺序编写或者是按顺序放置这些被拆分的方法,这样的话,下一个接替的人或是代码阅读者就很方便的看代码,他只需要自上而下往下自然翻阅就能看到整个逻辑流程了。而不是还需要花时间时而上时而下翻的查找代码。虽然现在很多的编译器能帮助你快速定位你方法的申明与引用,但是将同一模块逻辑的代码按需求顺序放置也是必要的,至少不会散落各处。
举个 .NET Core 团队编写代码的例子HostBuilder
仔细阅读我们就能发现他们就是按照这种方式来编写代码的
- 所有的私有字段放在开头
- 所有的属性放在一起
- 所有的方法放在一起
仔细看方法 IHost Build()
我们不难发现,并不是所有的 public 方法都是放在一起的,而是分逻辑块按调用顺序放置的。我们在阅读代码的时候,我们只需要从上而下翻阅即可,这对我们特别是没有IDE帮助的时候是特别友好的。
每一个小模块的方法都是自上而下的;