设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Go语言的10个实用技术

2014-8-27 10:13| 发布者: joejoe0332| 查看: 3639| 评论: 0|原作者: mikespook|来自: oschina

摘要: 这里是我过去几年中编写的大量 Go 代码的经验总结而来的自己的最佳实践。我相信它们具有弹性的。某个应用需要适配一个灵活的环境。你不希望每过 3 到 4 个月就不得不将它们全部重构一遍。添加新的特性应当很容易。许 ...

十条有用的 Go 技术


  这里是我过去几年中编写的大量 Go 代码的经验总结而来的自己的最佳实践。我相信它们具有弹性的。这里的弹性是指:


  某个应用需要适配一个灵活的环境。你不希望每过 3 到 4 个月就不得不将它们全部重构一遍。添加新的特性应当很容易。许多人参与开发该应用,它应当可以被理解,且维护简单。许多人使用该应用,bug 应该容易被发现并且可以快速的修复。我用了很长的时间学到了这些事情。其中的一些很微小,但对于许多事情都会有影响。


  所有这些都仅仅是建议,具体情况具体对待,并且如果有帮助的话务必告诉我。随时留言:)


 

1. 使用单一的 GOPATH

  多个 GOPATH 的情况并不具有弹性。GOPATH 本身就是高度自我完备的(通过导入路径)。有多个 GOPATH 会导致某些副作用,例如可能使用了给定的库的不同的版本。你可能在某个地方升级了它,但是其他地方却没有升级。而且,我还没遇到过任何一个需要使用多个 GOPATH 的情况。所以只使用单一的 GOPATH,这会提升你 Go 的开发进度。

  许多人不同意这一观点,接下来我会做一些澄清。像 etcd camlistore 这样的大项目使用了像 godep 这样的工具,将所有依赖保存到某个目录中。也就是说,这些项目自身有一个单一的 GOPATH。它们只能在这个目录里找到对应的版本。除非你的项目很大并且极为重要,否则不要为每个项目使用不同的 GOPATH。如果你认为项目需要一个自己的 GOPATH 目录,那么就创建它,否则不要尝试使用多个 GOPATH。它只会拖慢你的进度。


2. 将 for-select 封装到函数中

  如果在某个条件下,你需要从 for-select 中退出,就需要使用标签。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
 
L:
    for {
        select {
        case <-time.After(time.Second):
            fmt.Println("hello")
        default:
            break L
        }
    }
 
    fmt.Println("ending")
}

  如你所见,需要联合break使用标签。这有其用途,不过我不喜欢。这个例子中的 for 循环看起来很小,但是通常它们会更大,而判断break的条件也更为冗长。

  如果需要退出循环,我会将 for-select 封装到函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
    foo()
    fmt.Println("ending")
}
 
func foo() {
    for {
        select {
        case <-time.After(time.Second):
            fmt.Println("hello")
        default:
            return
        }
    }
}

  你还可以返回一个错误(或任何其他值),也是同样漂亮的,只需要:

1
2
3
4
// 阻塞
if err := foo(); err != nil {
    // 处理 err
}


3. 在初始化结构体时使用带有标签的语法

  这是一个无标签语法的例子:

1
2
3
4
5
6
7
8
9
type T struct {
    Foo string
    Bar int
}
 
func main() {
    t := T{"example"123// 无标签语法
    fmt.Printf("t %+v\n", t)
}

  那么如果你添加一个新的字段到T结构体,代码会编译失败:

1
2
3
4
5
6
7
8
9
10
type T struct {
    Foo string
    Bar int
    Qux string
}
 
func main() {
    t := T{"example"123// 无法编译
    fmt.Printf("t %+v\n", t)
}

  如果使用了标签语法,Go 的兼容性规则(http://golang.org/doc/go1compat)会处理代码。例如在向net包的类型添加叫做Zone的字段,参见:http://golang.org/doc/go1.1#library。回到我们的例子,使用标签语法:

1
2
3
4
5
6
7
8
9
10
type T struct {
    Foo string
    Bar int
    Qux string
}
 
func main() {
    t := T{Foo: "example", Qux: 123}
    fmt.Printf("t %+v\n", t)
}

  这个编译起来没问题,而且弹性也好。不论你如何添加其他字段到T结构体。你的代码总是能编译,并且在以后的 Go 的版本也可以保证这一点。只要在代码集中执行go vet,就可以发现所有的无标签的语法。


4. 将结构体的初始化拆分到多行

  如果有两个以上的字段,那么就用多行。它会让你的代码更加容易阅读,也就是说不要:

1
T{Foo: "example", Bar:someLongVariable, Qux:anotherLongVariable, B: forgetToAddThisToo}

  而是:

1
2
3
4
5
6
T{
    Foo: "example",
    Bar: someLongVariable,
    Qux: anotherLongVariable,
    B: forgetToAddThisToo,
}

  这有许多好处,首先它容易阅读,其次它使得允许或屏蔽字段初始化变得容易(只要注释或删除它们),最后添加其他字段也更容易(只要添加一行)。


5. 为整数常量添加 String() 方法

  如果你利用 iota 来使用自定义的整数枚举类型,务必要为其添加 String() 方法。例如,像这样:

1
2
3
4
5
6
7
8
type State int
 
const (
    Running State = iota
    Stopped
    Rebooting
    Terminated
)

  如果你创建了这个类型的一个变量,然后输出,会得到一个整数(http://play.golang.org/p/V5VVFB05HB):

1
2
3
4
5
6
func main() {
    state := Running
 
    // print: "state 0"
    fmt.Println("state ", state)
}

  除非你回顾常量定义,否则这里的0看起来毫无意义。只需要为State类型添加String()方法就可以修复这个问题(http://play.golang.org/p/ewMKl6K302):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func (s State) String() string {
    switch s {
    case Running:
        return "Running"
    case Stopped:
        return "Stopped"
    case Rebooting:
        return "Rebooting"
    case Terminated:
        return "Terminated"
    default:
        return "Unknown"
    }
}

  新的输出是:state: Running。显然现在看起来可读性好了很多。在你调试程序的时候,这会带来更多的便利。同时还可以在实现 MarshalJSON()、UnmarshalJSON() 这类方法的时候使用同样的手段。



酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部