GO 语言学习的五个阶段(带例子)

    /    2017-07-13

  Francesc (@francesc) 是 Go 核心团队的一员, 是提倡 Google Cloud 平台的开发者. 他是一个编程语言的爱好者, Google的技术指导大师, Go tour的创造者之一. 这个讨论的灵感来自于另一个 Raquel Vélez 在 JSConf. Slides 的讨论,这个讨论已经发到了这里.

  Sourcegraph 是下一代编程协作工具, 用于搜索, 探索, 和审查代码. 我们参加GopherCon India 来分享我们是怎样使用 Go 并学习别人是怎样使用它的, 对配合liveblog的这次讨论我们深感荣幸.

  作为Go团队的开发者之一,Francesc可能比世界上其他人接触到的Go语言程序员都要多。正因为有了这样的有利条件,他把Go语言的学习过程划分为5个阶段。

  这些阶段对于其他语言的学习也是成立的。理解自己处于哪个阶段,可以帮助你找到提高自己的最有效的方法,也可以避免每个阶段学习过程中的常见陷阱。

  这里是GO程序员的五个进化阶段:

  第一个阶段(菜逼): 刚刚学习了这门语言。 已经通过一些教程或者培训班了解基本的语法,可以写短的代码片段。

  第二个阶段 (探索者): 可以写一个完整的程序,但不懂一些更高级的语言特征,比如“channels”。还没有使用GO写一个大项目。

  第三个阶段(大手): 你能熟练的使用Go, 能够用GO去解决,生产环境中一个具体和完整的问题。已经形成了一套自己的惯用法和常用代码库。在你的编码方案中Go是一个非常好用的工具。

  第四阶段 (大神): 绝逼清楚Go语言的设计选择和背后的动机。能理解的简洁和可组合性哲学。

  布道师: 积极地与他人分享关于Go语言知识和你对Go语言的理解。在各种合适的场所发出自己的声音, 参与邮件列表、建立QQ群、做专题报告。成为一个布道者不见得是一个完全独立的阶段,这个角色可以在上述的任何一个阶段中。

  第一阶段: 菜逼

  菜鸟在这个阶段使用Go去创建一些小项目或者玩具项目。他们应该会利用到Go tour, Go playground, Go文档, 和邮件列表(golang-nuts).

  func main() {

  fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))}

  查看上下文

  func main in golang/example on ✱ Sourcegraph

  这是Go语言写的简单例子,这个代码段来自golang/example 代码库里面的 hello.go 。 点击就可以查看完整代码撸。

  一项重要的技能,新人应该试着学习如何正确提问。很多新人在邮件列表里面这样说“嘿,这报错了”,这并没有提供足够的信息,让别人能理解并帮助他们解决问题。别人看到的是一个粘贴了几百行的代码的帖子,并没有花费精力来重点说明所遇到的问题。

  所以, 应该尽量避免直接粘贴代码到论坛。而应该使用可以编辑并且可以在浏览器中直接运行的Go playground的“分享”按钮链接到代码片段。

  探索者已经可以使用Go写一些小的软件,但有时仍然会有些迷茫。他们可能不完全明白怎么使用Go的高级特性,比如通道。虽然他们还有很多东西要学习,但已掌握的足够做一些有用的事情了!他们开始对Go的潜能有感觉了,并对它们能使用Go创建的东西感到兴奋。

  在探索阶段通常会经历两个步骤。第一,膨胀的预期达到顶点,你觉得可以用Go做所有的事情,但还并不能明白或领悟到Go的真谛。你大概会用所熟悉的语言的模式和惯用语来写Go代码,但对于什么是地道的Go,还没有比较强烈的感觉。你开始尝试着手干这样的事情--“迁移架构X,从Y语言到Go语言”。

  到达预期膨胀的顶点之后,你会遇到理想幻灭的低谷。你开始想念语言Y的特性X,此时你还没有完全的掌握地道的Go。你还在用其他编程语言的风格来写Go语言的程序,你甚至开始觉得沮丧。你可能在大量使用reflect和unsafe这两个包,但这不是地道的Go。地道的Go不会使用那些魔法一样的东西。

  这个探索阶段产生的项目的一个很好的例子就是Martini Web框架。Martini是一个Go语言的早期Web框架,它从Ruby的Web框架当中吸收了很多思想(比如依赖注入)。最初,这个框架在社区中引起了强烈的反响,但是它逐渐在性能和可调试性上受到了一些批评。Martini框架的作者Jeremy Saenz积极响应这些来自Go社区的反馈,写了一个更加符合Go语言规范的库Negroni

  func (m *Martini) RunOnAddr(addr string) {

  // TODO: Should probably be implemented using a new instance of http.Server in place of

  // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use.

  // This would also allow to improve testing when a custom host and port are passed.

  logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger)

  logger.Printf("listening on %s (%s)\n", addr, Env)

  logger.Fatalln(http.ListenAndServe(addr, m))}

  查看上下文

  (*Martini).RunOnAddr in go-martini/martini on ✱ Sourcegraph

  来自Martini框架的交互式代码片段,它是不地道的Go的例子。注意用反射包实现的依赖注入

  func TestNegroniServeHTTP(t *testing.T) {

  result := ""

  response := httptest.NewRecorder()

  n := New()

  n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

  result += "foo"

  next(rw, r)

  result += "ban"

  }))

  n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

  result += "bar"

  next(rw, r)

  result += "baz"

  }))

  n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

  result += "bat"

  rw.WriteHeader(http.StatusBadRequest)

  }))

  n.ServeHTTP(response, (*http.Request)(nil))

  expect(t, result, "foobarbatbazban")

  expect(t, response.Code, http.StatusBadRequest)}

  查看上下文

  func TestNegroniServeHTTP in codegangsta/negroni on * Sourcegraph

  来自Negroni库的交互式代码片段,它是地道的Go的例子

  其他语言在提供一些核心功能,比如HTTP处理的时候,往往需要依赖第三方库。但是Go语言在这一点上很不同,它的标准库非常强大。如果你认为Go标准库没有强大到可以做你想做的事情,那么我说你错了。Go语言标准库难以置信的强大,值得你花时间阅读它的代码,学习它实现的模式。

  func (srv *Server) ListenAndServe() error {

  addr := srv.Addr

  if addr == "" {

  addr = ":http"

  }

  ln, err := net.Listen("tcp", addr)

  if err != nil {

  return err

  }

  return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}

  在上下文中查看

  (*Server).ListenAndServe in golang/go on* Sourcegraph

  Go标准库中的ListenAndServe函数片段。如果你写过Go程序,你可能已经调用过这个函数很多次了,但是你曾经花时间看过它的实现么?去点击上面的代码片段吧。

  幻灭的低谷中的幻灭感来自于这样的事实:你还在用其他语言的模式来想问题,而且你还没有完全探索过Go能提供给你什么。下面是一些好玩的事情,你可以做一下来打破困境,进一步探索这门语言中好玩的事。

  go generate

  现在来看看go generate。go generate是一个你可以用来自动自成Go代码的命令。你可以结合例如jsonenums(一个用于为枚举类型自动生成JSON编组样板代码的类库)这样的元编程来使用go generate快速自动实现重复乏味代码的编写。在Go标准类库里面已经有大量可以用于解析AST的接口,而AST使得编写元编程工具更简单,更容易。在会议上,有另外两次讨论(Go语言中的元编程实践和拥抱标准类库)谈及到了这一点。

  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命令。

  暗夜在火星

  暗夜在火星

  翻译于 2年前

  3人顶

  顶 翻译得不错哦!

  OpenGL

  许多人使用Go作web服务,但是你知道你也可以用Go写出很cool的图形应用吗?查看Go在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。

(12)

分享至