Effective Go summary

ninetyhe

格式

程序统⼀使⽤ ”gofmt“ 进行格式化;如果使用的IDE是Goland可以按照如下进行配置:PrePerences -> Tools -> FileWatcher(如果没有在plugins安装) :添加 go fmt;

推荐一并添加:golangci-lint 和 goimports。这样就可以进⾏语法检查和⾃动处理导入和删除包注释

// ⾏注释⽅式,⽤于简单的方法和字段注释

/* */ 块注释方式,本书推荐在每一个包前面注释,说明该包的主要作用

  /*

  function: do xxx

  */

  package regexp

名字

Go语⾔言的命名⼤小写,决定了该方法或者该类是否公有还是私有,本书推荐如果⽅法不对外调用,首字⺟建议小写,如果需要跨包调⽤则⾸字⺟需要大写;

包的命名统一使⽤用小写,并且不应该使⽤下划线或者驼峰。(这点表示有点别扭,如果名字真的过长的话,可读性会很差) Get/Set:Go不提供对Get和Set支持,但是推荐⾃己实现,但是对于Get建议直接⽤⾸字母代替,例如:如果你有一个域叫做owner(⼩写,不被导出),则Get方法应该叫做 Owner (大写,被导出),⽽不是 GetOwner (个人觉得有点反人类,⽽且protobuf generate 的pb文件的字段也是是 Getxx )。这里联想到⽬前我们⾃⼰用的gRPC,看到很多同学的字段命名都是大驼峰的,开始还以为gRPC有特殊不同的规范,后来查阅了官网更加肯定,对象的属性要⼩驼峰,字段的访问需要通过Get和Set来访问!⽅法命名:单个方法的接⼝口使⽤方法名加上“er”后缀来命名,或者类似的修改来构造⼀个施动者名词

分号

Go语⾔省略分号; {} 中的左 { 不能换行;条件语句省略 ()

控制结构

Go中的循环结构只用 for 语句,主要分为以下三种:

// 传统的for写法 for init; condition; post { }

// 对应其他语言中 while for condition { }

//⼀直循环 for { }

函数

⽀持多返回值,Go语⾔支持函数返回多个返回值,本书推荐也给返回值命名,这样对于以下int值对应哪

个就值就⼀⽬了然 例例如:

func (file *File) Write(b []byte) (n int, m int, err error)

延时执行defer;这是一种不不寻常但又很有效的方法,⽤于处理类似于不管函数通过哪个执行路径返回,

资源都必须要被释放的情况。经常⽤用于链接断开,释放锁或者关闭IO如果需要将⼀些值初始化,或者在代码执行前调⽤,可以在 init 函数实现,该函数会优先被执⾏;

数据

Go语⾔言使⽤new和make分配原语; new ⽤于给T类型的分配内存,但是并不会初始化内存,只是将其置为0; make 不同于 new ,它用于创建 silence , map 和 channel ,并返回一个初始化(⽽不是置0),类型给T的值。如果需要获得一个显式的指针,就必须只⽤用 new 分配。

构造器 :本书推荐结构体的属性值尽量不要有空或者空,通过构造器对其初始化;

数组 :在Go中:数组是值。将⼀个数组赋值给另一个,会拷⻉所有的元素;特别是,如果你给函数传递⼀个数组,其将收到一个数组的拷贝,⽽不是它的指针;组的大小是其类型的一部分。类型

[10]int 和 [20]int 是不同的。

切片 :切⽚持有对底层数组的引用,如果你将⼀个切⽚赋值给另⼀个,⼆者都将引用同⼀个数组,或你将切片的值进行修改,则原数组的内容也将发⽣改变

字典 :同切片一样,map持有对底层数据结构的引用。如果将map传递给函数,其对map的内容做了改变,则这些改变对于调⽤者是可⻅的。如果像判断Map释放包含该元素,需要使用 :

//感觉好蠢,并没有像Java那样有contains key或者contains value,//如果像⽅便使⽤且避免代码写成一坨,建议⾃⼰实现一个⽅法收敛该逻辑if value,ok := map[key];ok{

// if exist do some

}

//⽽不建议使⽤以下⽅式:value, ret := map1["key"]

if ret == true{

fmt.Print("map1[\"key\"]存在,值为:", i)

} else {

fmt.Print("map1[\"key\"],不存在\n")

}

初始化

常量 在编译的时候就被创建,并且只能是数字,字符(附文),字符串或者布尔类型。由于编译限制,

表达式必须为能被编译器求值的常量表达式, 例如:// 常量表达式

1<<3

// ⾮常量表达式,在const不可使用math.Sin(math.Pi/4)

init函数 如果需要将一些值初始化,或者需要预先调⽤⽅法函数,可以在 int 函数中实现;例如:

func init() {

//需要初始化调用方法

initFunction(a interface{},b interface{})

// 初始化变量

initParam

}

接⼝

通过法与接口,Go语⾔定义了一种与java/C++等OOP语言截然不同的“继承”的形态。通过实现接口定

义的方法,便可将reciever的类型变量赋值给接⼝类型变量,通过接⼝类型变量来调用到reciever类型的

⽅法 例如:

//定义了一个接口geometry表示几何类型type thisIsInterface interface {

    funcA() int32

    funcB(bool)

}

//实现类实现两个方法type IntImpl struct {

A, B float64

}

//在Go中,实现接⼝,只需要实现该接口定义的所有方法即可

//A接口方法实现

  func (r * IntImpl) funcA(res float64) {

    A *= res

    B *= res

}

func (r * IntImpl) funcB() float64 {

    return r.A * r.B

}

//可以把rect和circle类型的变量量作为实参

//传递给geometry接口类型的变量

func measure (i thisIsInterface){

    fmt.Print("i 's area:",i.funcA(),"\n")

    i.funcB(2)

    fmt.Print("after funcB , funcA :",i.funcA(),"\n")

}

func main() {

    i := IntImpl{A: 10, B: 5}

    measure(&i)

}

并发

Gorutine 轻量级并发的函数执行线索,创建开销初始化栈空间⽐较⼩,会根据实际需要堆的空间分配或

者释放额外空间Gorutine与操作系统线程间采用“多对多”的映射⽅方式,其他Gorutines不会因为其中⼀一个Gorutines阻塞后

⽽阻塞

在函数前加 go 关键字就可以创建⼀个Gorutine并调⽤该函数⽅法。当该函数执行完成之后隐式推出(类似于Java中的 Thread )

Channel:于map类似,也是通过 make 进⾏行行分配,如果创建的时候指定⼤小,如果⾮零则即创建一个

缓冲区。如果是零,则是⽆无缓冲的channel或同步channel

例例如:

stringBuffer := make(chan string,10)

value := <- stringBuffer

并行实现方式:可以创建多个go func 并行执⾏多个函数,然后通过sync.WaitGroup来实现阻塞等并⾏的结果(类似于Java中的 CountDownlunch ),但是功能相当简陋陋,功能相差更更远

互斥: 通过sync.Mutex可以创建锁进行互斥,例例如:

import (

"sync"

"runtime"

"fmt"

)

var {

//共享变量

num int

wg sync.WaitGroup

//锁

lock sync.Mutex

}

func Add(){

    defer wg.Done()

    for count := 0;ciunt < 2; count++{

        // lock

}

}

lock.Lock()

{

    value := num

    value++

    num = value

}

// release lock

lock.Unlock()

func main{

    wg.Add(2)

    go Add()

    go Add()

    wg.Wait()

    fmt.Printf("result is : %d\n", num)

}

结果如下:

result is : 4

类型断⾔

Go⾥面对类型判断有两种: value,ok := element.(T) 当然也可以直接这样用 (不推

荐) value:=element.(T) 这样的话,⼀旦出错就会产⽣运⾏错误,不推荐使用;

你可能感兴趣的:(Effective Go summary)