如何提高golang的可读性

1. 尽早返回

反例:

//UserCtrl
func UserInfo(userId string){
  user.UserInfo(userId)
  ....
  ....
  //resp result ...
}

//UserService
func UserInfo(userId string){
  if len(userId) > 0 { 
    //do query database 
    .....
  } 
}
// repo
func queryUserInfo(userId string){
  if len(userId) > 0{
    //select * from user where user_id = ?
  }
}

从这个例子来看,在service层和数据库查询,我们都进行了userId的判断. 因为当我们经常会忘记,我们是否在上一层入参的时候进行了userId为空的判断. 为了避免空指针,我们不得已一层层进行判断.

假如我们尽早地返回,那么就可以避免后续的层层判空

推荐写法:

//userCtrl
func UserInfo(userId string){
  if len(userId) == 0 {
    //resp some error
  }
  user.UserInfo(userId)
}

2. 写好分支语句

反例:

func xxx(lang string){
  if lang == "java"{
    doA()
  } else {
    doB()
  }
  
  if lang == "java"{
    doC()
  }else{
    doD()
  }
}

推荐写法:

func java(lang string){
  doA()
  doC()
}

func other(lang string){
  doB()
  doC()
}

if都需要包含else

反例:

if cmd == "1" {
  if status == 0 {
    doSome()
  }
}else {
  if tag == 1 {
    doSomeB()
  }
}

推荐写法:

if cmd == "1" {
  if status == 0 {
    
  }else{
    
  }
}else {
  if tag == 1{
    
  }else {
    
  }
}

避免啰嗦的条件

if isDone() == true {
  do()
}

推荐:

if isDone() {
  doSome()
}

使用switch语句

反例:

if lang == "java"{
  
}else if lang == "c#" {
  
}else if lang == "clojure"{
  
}

推荐写法:

switch lang:
case "java":
case "c#":
case "clojure":

减少逻辑表达式:

逻辑表达式是门电路的表达式,总会有人不能记得他的先后执行顺序。

反例:

if lang == "java" || lang == "c#" && lang == "clojure" {
  doSome()
} 

如果非得使用逻辑表达式,推荐使用括号,显示地说明调用的顺序.

if (lang == "java" || lang == "c#") && lang == "clojure" {
  doSome()
} 

使用正序的逻辑

反例:

if !isUserInfoSaved() {
  doA()
}else {
  doB()
}

if !isNotStop() {
  doSome()
}

用一个符合人类思考顺序方式来写分支,减少阅读代码时的时间

if isUserInfoSaved() {
  doB()
}else {
  doA()
}

if isStart(){
  doSome()
}

3.合理使用局部变量

可能我们知道定义一个变量会开辟一块新的内存,有时候觉得自己重复使用一个变量,会让性能"好一些", 于是我们就会写出下面的代码

反例:

var name
if login {
  name = "user"
  doSome(name)
}else {
  name = "guest"
  doSome(name)
}

其实在栈上开辟内存的成本很低,编译器会对代码进行逃逸分析,而且执行完这个方法后,内存就会被回收掉,所以不用担心这个性能问题.

推荐写法:


if login {
    //使用局部变量
  name := "user"
  doSome(name)
}else {
  name := "guest"
  doSome(name)
}

有的时候我们会想耍个酷,那么牛逼的调用一行代码就写完了,可是这时候阅读起来是非常痛苦的一件事情.

反例:

saveXXX(queryRole(),queryOrder(),saveXXX(queryUserInfo(genUserId())))

推荐写法:

我们把参数通过一个中间变量存起来,这样会很明显地说明,我们都干了些什么.

userInfo := queryUserInfo(genUserId)
u := saveXXX(userInfo)
saveXXX(queryRole(),queryOrder(),u)

4.循环

循环本身就不好读,假如在循环中包含continue,break之类的,让原本的代码更难读

反例:

for i,itm := range Users {
  if item.Name != "admin" {
    continue
  }else {
    doSome()
  }
}

推荐写法:

for i,itm := range Users {
  if item.Name == "admin" {
    doSome()
  }
}

使用i,j之类的下标,本身就比较相似,一不小心就会造成了下标越界,推荐使用foreach

反例:

for i:=0 ; i< len(users); i++ {
   for j:=0 ; j < len(users[i].children); j++ {
     // doSome
   }
}

推荐写法

for _,itm := range users {
  for _, c := range item{
    //dosome
  }
}

假如非得使用下标操作,也要避免使用i,j之类的变量

for uidx :=0 ; uidx< len(users); uidx++ {
   childen := users[uidx].children  //使用中间变量,避免臃肿
   for chidx:=0 ; chidx < len(childen); chidx++ {
       // chidx 和 uidx 能避免混淆,能在使用的过程中避免出错。
   }
}

5.面条代码, 过程式代码

面条代码过程式代码

type UserInf struct {
  UserName   string
  UserId     string
  Role       *Role
  Alias     string
}

type Role struct {
  Id string
  Name string
}

func save(inf *UserInf){
  Role(inf)
  inf.Alias = genAlias()
  update(inf)
}

func update(inf *UserInf){
  //update ....
}

func Role(inf *UserInfo) {
  queryUser(inf)
  inf.Role = queryRole(inf.UserId)
}

func queryUser(inf *UserInf){
  //select * from user where user_id = ?
  inf.UserName = ...
  inf.xxx = ...
}

思考: 为什么要使用纯函数?

6. 控制代码的长度

人的左脑关心的是逻辑,右脑做的是快照(可能是伪科学),实际情况中,假如我们一眼能看完,是不是剩下的就是在想逻辑,而不是一边读代码,一边想逻辑,这样能让我们大脑一次性把看到的代码缓存起来,然后专注于想逻辑。简短的代码,也可以避免bug,所以方法建议都控制在100行之内。

7. 命名

这个放最后来写的原因是这个命名本来就很难,命名得好就会让代码清晰可读,命名不好,就会误导导致需要大量的注视来注释代码,本来维护代码已经是一件痛苦的事情了,假如在修改了代码后,注释没有同步修改,反而会引起误导。因为母语不是英文,很多同学跟我一样也都很痛苦,这里可以上网找找相关命名的资料,本人水平有限也只能是大概地举几个例子

bool isStart // 服务启动的状态,最好使用正向的表达,是否启动,不推荐使用  bool isNotStop
func ComputeUserScore() //计算用户积分,假如这是一个耗时的操作,推荐在方法名上就表示出来,不推荐使用GetUserScore,
func DownloadFile() //下载文件,不推荐使用GetFile

个人博客 https://youkale.github.io

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容