140 个字符提示
- leave your object oriented brain at home. Embrace the interface. @mikegehard
别想着面向对象了,拥抱接口吧。
- Learn to do things the Go way, don’t try to force your language idioms into Go. @DrNic
学习用 Go 语言的方式做事,不要试图把你的语言习性强加给 Go。
- It’s better to over do it with using interfaces than use too few of them. @evanphx
接口使用宜多不宜少。
- Embrace the language: simplicity, concurrency, and composition. @francesc
拥抱这门语言:简单、并发和组合。
- read all the awesome docs that they have on golang.org. @vbatts
阅读他们在 golang.org 网站上的所有精彩文档。
- always use
gofmt
. @darkhelmetlive
多多使用 gofmt
- read a lot of source code. @DrNic
阅读大量的源码。
- Learn and become familiar with tools and utilities, or create your own! They are as vital to your success as knowing the language. @coreyprak
学习并熟悉工具和实用程序,或创建自己的工具和实用程序!它们对您的成功与了解语言一样重要
额外导入包的方法
还有其他几种导入包的方法。我们将在以下示例中使用 fmt 包:
import format "fmt"
: 创建fmt
的别名. 然后在所有使用fmt
包内容的地方使用format.
而不是fmt.
如format.Println("Hello,World")
。import . "fmt"
: 多了个点号.
,这种方式允许直接访问包的内容,无需在其前面加上fmt
。import _ "fmt"
- 如果未使用fmt
,则抑制与fmt
相关的编译器警告,如果有则执行初始化函数。fmt
的其余部分无法访问。
goimports
Goimports 是一种更新 Go 导入行、添加缺失行和删除未引用行的工具。
它的作用与 gofmt(插入式替换)相同,但除了代码格式之外,还修复了导入。
代码组织
Go 是一种非常简单的编程语言,但首先是开发人员最难的事情是如何组织代码。由于许多原因,轨道变得流行,并且脚手架是其中之一。它给出了新的开发人员清晰的指示和地方,以便将其代码和成语遵循。
在某种程度上,Go 通过为开发人员提供很棒的工具(如 go fmt)以及不会编译未使用的变量或未使用的导入语句的严格编译器来做同样的事情。
自定义构造函数
我经常听到的一个问题是我应该什么时候使用像 NewJob
这样的自定义构造函数。我的答案是,在大多数情况下你不需要。但是,每当您需要在初始化时间设置值并且您有某种默认值时,它都是构造函数的好候选者。在上面的示例中,添加构造函数会产生很多意义,因此我们可以设置默认记录器。
package main import ( "log" "os" ) type Job struct { Command string *log.Logger } func NewJob(command string) *Job { return &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)} } func main() { NewJob("demo").Print("starting now...") }
集合
您可能希望找到一种方法来从集合中提取唯一值。用其他语言,您通常具有不允许重复的集数据结构。Go 没有内置的,但它不太难以实现(由于缺乏泛型,你需要为大多数类型做到这一点,这可能是麻烦的)。
// UniqStr returns a copy if the passed slice with only unique string results. func UniqStr(col []string) []string { m := map[string]struct{}{} for _, v := range col { if _, ok := m[v]; !ok { m[v] = struct{}{} } } list := make([]string, len(m)) i := 0 for v := range m { list[i] = v i++ } return list }
我使用了一些有趣的技巧。首先,空结构体的映射:
m := map[string]struct{}{}
我们创建了一个映射,键是我们想要唯一的值,关联的值并不重要,所以它可以是任何东西。例如:
m := map[string]bool{}
然而,我选择了一个空结构,因为它会像布尔值一样快,但不会分配那么多内存。
第二个技巧可以看得更清楚一点:
if _, ok := m[v]; !ok { m[v] = struct{}{} }
我们在这里所做的,只是检查映射 m 中是否有与键 v 关联的值,我们不关心值本身,但是如果我们知道我们没有值,那么我们添加一个。
一旦我们有了一个带有唯一键的映射,我们就可以将它们提取到一个新的字符串切片中并返回结果。
下面是这个函数的测试,如你所见,我使用了一个表测试,这是运行单元测试的惯用 Go 方式:
func TestUniqStr(t *testing.T) { data := []struct{ in, out []string }{ {[]string{}, []string{}}, {[]string{"", "", ""}, []string{""}}, {[]string{"a", "a"}, []string{"a"}}, {[]string{"a", "b", "a"}, []string{"a", "b"}}, {[]string{"a", "b", "a", "b"}, []string{"a", "b"}}, {[]string{"a", "b", "b", "a", "b"}, []string{"a", "b"}}, {[]string{"a", "a", "b", "b", "a", "b"}, []string{"a", "b"}}, {[]string{"a", "b", "c", "a", "b", "c"}, []string{"a", "b", "c"}}, } for _, exp := range data { res := UniqStr(exp.in) if !reflect.DeepEqual(res, exp.out) { t.Fatalf("%q didn't match %q\n", res, exp.out) } } }
依赖包管理
不幸的是,Go 没有自带依赖包管理系统。可能是因为它起源于 C 文化,包没有版本化,也没有解决显式的版本依赖问题。
挑战是,如果您的项目上有多个开发人员,则希望所有这些都在依赖项的同一版本上。您的依赖项也可能有自己的依赖性,并且您希望确保一切顺利。当您使用不同版本的相同依赖项时,它会产生甚至欺骗者。这通常是 CI 环境中的情况。
在我们的 CI 环境中,我们在运行测试套件之前将 GOPATH 设置为项目特定的文件夹,因此项目之间不会共享包。
善用错误
错误是 Go 中非常重要的模式,起初,新开发人员对返回值和错误的函数数量感到惊讶。
Go 没有您可能在其他编程语言中看到的异常概念。 Go 确实有一种叫做 panic
的东西,但正如它的名字所暗示的那样,它们真的很特别,不应该被拯救(也就是说,它们可以)。
首先,Go 的错误处理似乎繁琐和重复,但迅速成为我们思考方式的一部分。而不是创建泡起来且可能或可能不会处理或通过更高的异常,而是错误是响应的一部分,并且旨在由呼叫者处理。每当函数可能会生成错误时,其响应应包含错误参数。