Francesc (@francesc) 是 Go 核心团队的一员, 是提倡 Google Cloud 平台的开发者. 他是一个编程语言的爱好者, Google的技术指导大师, Go tour的创造者之一. 这个讨论的灵感来自于另一个 Raquel Vélez 在 JSConf. Slides 的讨论,这个讨论已经发到了这里. Sourcegraph 是下一代编程协作工具, 用于搜索, 探索, 和审查代码. 我们参加GopherCon India 来分享我们是怎样使用 Go 并学习别人是怎样使用它的, 对配合liveblog的这次讨论我们深感荣幸. |
作为Go团队的开发者之一,Francesc可能比世界上其他人接触到的Go语言程序员都要多。正因为有了这样的有利条件,他把Go语言的学习过程划分为5个阶段。 这些阶段对于其他语言的学习也是成立的。理解自己处于哪个阶段,可以帮助你找到提高自己的最有效的方法,也可以避免每个阶段学习过程中的常见陷阱。 编者按:这篇文章对于每一个学习阶段都给出了交互式的代码片段。点击函数名你就可以跳到具体的函数定义,方便进行深入的研究。请看下文。 |
这里是GO程序员的五个进化阶段:
|
第一阶段: 菜逼
|
一项重要的技能,新人应该试着学习如何正确提问。很多新人在邮件列表里面这样说“嘿,这报错了”,这并没有提供足够的信息,让别人能理解并帮助他们解决问题。别人看到的是一个粘贴了几百行的代码的帖子,并没有花费精力来重点说明所遇到的问题。 所以, 应该尽量避免直接粘贴代码到论坛。而应该使用可以编辑并且可以在浏览器中直接运行的Go playground的“分享”按钮链接到代码片段。 |
Phase 2: the explorer
|
到达预期膨胀的顶点之后,你会遇到理想幻灭的低谷。你开始想念语言Y的特性X,此时你还没有完全的掌握地道的Go。你还在用其他编程语言的风格来写Go语言的程序,你甚至开始觉得沮丧。你可能在大量使用reflect和unsafe这两个包,但这不是地道的Go。地道的Go不会使用那些魔法一样的东西。 这个探索阶段产生的项目的一个很好的例子就是Martini Web框架。Martini是一个Go语言的早期Web框架,它从Ruby的Web框架当中吸收了很多思想(比如依赖注入)。最初,这个框架在社区中引起了强烈的反响,但是它逐渐在性能和可调试性上受到了一些批评。Martini框架的作者Jeremy Saenz积极响应这些来自Go社区的反馈,写了一个更加符合Go语言规范的库Negroni
(*Martini).RunOnAddr in go-martini/martini on ✱ Sourcegraph 来自Martini框架的交互式代码片段,它是不地道的Go的例子。注意用反射包实现的依赖注入
func TestNegroniServeHTTP in codegangsta/negroni on * Sourcegraph 来自Negroni库的交互式代码片段,它是地道的Go的例子 |
其他语言在提供一些核心功能,比如HTTP处理的时候,往往需要依赖第三方库。但是Go语言在这一点上很不同,它的标准库非常强大。如果你认为Go标准库没有强大到可以做你想做的事情,那么我说你错了。Go语言标准库难以置信的强大,值得你花时间阅读它的代码,学习它实现的模式。
(*Server).ListenAndServe in golang/go on* Sourcegraph Go标准库中的ListenAndServe函数片段。如果你写过Go程序,你可能已经调用过这个函数很多次了,但是你曾经花时间看过它的实现么?去点击上面的代码片段吧。 幻灭的低谷中的幻灭感来自于这样的事实:你还在用其他语言的模式来想问题,而且你还没有完全探索过Go能提供给你什么。下面是一些好玩的事情,你可以做一下来打破困境,进一步探索这门语言中好玩的事。 |
go generate
|
func main() { |
flag.Parse() if len(*typeNames) == 0 { log .Fatalf( "the flag -type must be set" ) } types := strings.Split(*typeNames, "," ) // Only one directory at a time can be processed, and the default is ".". dir := "." if args := flag.Args(); len(args) == 1 { dir = args[0] } else if len(args) > 1 { log .Fatalf( "only one directory at a time" ) } pkg, err := parser.ParsePackage(dir, *outputSuffix+ ".go" ) if err != nil { log .Fatalf( "parsing package: %v" , err) } var analysis = struct { Command string PackageName string TypesAndValues map[string][]string }{ Command: strings.Join(os.Args[1:], " " ), PackageName: pkg.Name, TypesAndValues: make(map[string][]string), } // Run generate for each type. for _, typeName := range types { values, err := pkg.ValuesOfType(typeName) if err != nil { log .Fatalf( "finding values for type %v: %v" , typeName, err) } analysis.TypesAndValues[typeName] = values var buf bytes.Buffer if err := generatedTmpl.Execute(&buf, analysis); err != nil { log .Fatalf( "generating code: %v" , err) } src, err := format.Source(buf.Bytes()) if err != nil { // Should never happen, but can arise when developing this code. // The user can compile the output to see the error. log .Printf( "warning: internal error: invalid Go generated: %s" , err) log .Printf( "warning: compile the package to analyze the error" ) src = buf.Bytes() } output := strings.ToLower(typeName + *outputSuffix + ".go" ) outputPath := filepath.Join(dir, output) if err := ioutil.WriteFile(outputPath, src, 0644); err != nil { log .Fatalf( "writing output: %s" , err) } }} |
✱ Sourcegraph 站点上 campoy/jsonenums 中的 main 函数
一段互动的片段演示了如何编写jsonenums命令。
OpenGL
|
func main() { |
glfw.SetErrorCallback(errorCallback) if !glfw.Init() { panic( "Can't init glfw!" ) } defer glfw.Terminate() window, err := glfw.CreateWindow(Width, Height, Title, nil, nil) if err != nil { panic(err) } window.MakeContextCurrent() glfw.SwapInterval(1) gl.Init() if err := initScene(); err != nil { fmt.Fprintf(os.Stderr, "init: %s\n" , err) return } defer destroyScene() for !window.ShouldClose() { drawScene() window.SwapBuffers() glfw.PollEvents() }} |
在 ✱ Sourcegraph中 go-gl/examples 里面的函数 main
交互式的片段正说明Go的OpenGL捆绑能制作Gopher cube。点击函数或方法名去探索。
黑客马拉松和挑战
你也可以观看挑战和黑客马拉松,类似Gopher Gala和Go Challenge。在过去,来自世界各地的程序员一起挑战一些真实的酷项目,你可以从中获取灵感。
第三阶段: 老手作为一个老手,这意味着你可以解决很多Go语言中你关心的问题。新的需要解决的问题会带来新的疑问,经过试错,你学会了在这门语言中什么是可以做的,什么是不能做的。此时,你已经对这门语言的习惯和模式有了一个坚实的理解。你可以非常高效地工作,写出可读,文档完善,可维护的代码。 成为老手的一个很好的方法就是在大项目上工作。如果你自己有一个项目的想法,开始动手去做吧(当然你要确定它并不是已经存在了)。大多数人也许并没有一个很大的项目的想法,所以他们可以对已经存在的项目做出贡献。Go语言已经有很多大型项目,而且它们正在被广泛使用,比如Docker, Kubernetes和Go本身。可以看看这个项目列表
(*DockerCli).CmdRestart in docker/docker on * Sourcegraph Docker项目的交互式代码片段。点击函数名,开始探索之旅吧。 |
老手应该对Go生态系统的工具有一个很强的掌握,因为这些工具真的提高生产效率。你应该了解go generate,go vet,go test-race, 和gofmt/goimports/goreturns。你应该使用go fmt,因为它会自动把你的代码按照Go社区的风格标准来格式化。goimports可以做同样的事情,而且还会添加丢失的imports。goretures不光做了前面所说的事情,还可以在返回表达式添加丢失的错误,这是大家都讨厌的地方。 在老手阶段,你一定要开始做code review。code review的意义并不是要修改或者找到错误(那是测试人员做的事情)。code review可以帮助维持统一的编程风格,提高软件的总体质量,还可以在别人的反馈中提高你自己的编程技术。几乎所有的大型开源项目都对每一个提交做code review。 下面是一个从人类的反馈当中学习的例子:Google的Go团队以前都在main函数的外面声明命令行标记。在去年的GopherCon会议上,Francesc遇到了SoundCloud公司的Peter Bourgon(@peterbourgon)。Peter Bourgon说在SoundCloud,他们都在main函数内部声明标记,这样他们不会错误地在外部使用标记。Francesc现在认为这是最佳实践。 |
第四阶段:专家
|
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call { |
call := new (Call) call.ServiceMethod = serviceMethod call.Args = args call.Reply = reply if done == nil { done = make(chan *Call, 10) // buffered. } else { // If caller passes done != nil, it must arrange that // done has enough buffer for the number of simultaneous // RPCs that will be using that channel. If the channel // is totally unbuffered, it's best not to run at all. if cap(done) == 0 { log .Panic( "rpc: done channel is unbuffered" ) } } call.Done = done client.send(call) return call} |
✱ Sourcegraph 站点上 golang/go 中的 (*Client).Go
来自标准类库的一小块交互代码片段使用了频道。理解标准类库里面的模式背后的决策原因是成为一个专家必经之路。
但是不要成为只局限于单一语言的专家。跟其他任何语言一样,Go仅仅只是一个工具。你还应该去探索其他语言,并且学习他们的模式和风格。Francesc从他使用Go的经验中找到了编写JavaScript的启发。他还喜欢重点关注于不可变性和致力于避免易变性的Haskell语言,并从中获得了如何编写Go代码的灵感。
布道者作为一个布道者,你分享自己的知识,传授你学会的和你提出的最佳实践。你可以分享自己对Go喜欢或者不喜欢的地方。全世界各地都有Go会议,找到离你最近的。 你可以在任何一个阶段成为布道者,不要等到你成为这个领域的专家的时候才发出自己的声音。在你学习Go的任何一个阶段,提出问题,结合你的经验给出反馈,不要羞于提出自己不喜欢的地方。你提出的反馈可以帮助社区改善做事情的方法,也可能改变你自己对编程的看法。
func main in golang/tools on ✱ Sourcegraph 流行的present命令的main函数,很多Go的用户使用它来制作幻灯片。许多演讲者修改了这个模块来满足自己的需要。 |
Q&A问:在GO语言中,我所怀念的一项功能是一个好的调试器。 答:我们正在做了,不只是调试器,我们还会提供一个更好的总体监视工具可以让你在程序运行时更好地洞察程序在干什么(显示出所有正在运行的goroutine的状态)。在GO 1.5中探索它吧。 |
文章转载自:开源中国社区 [http://www.oschina.net]
本文标题:GO 语言学习的五个阶段(带例子)
本文地址:http://www.oschina.net/translate/the-5-stages-of-learning-go-with-examples
参与翻译:泥土笨笨, 砼砼, 社会主义好, 暗夜在火星, ShowColors, soaring, 无若
英文原文:The 5 stages of learning Go (with examples)