Golang 受欢迎的原因:大道至简

前言

Golang自2009年发布第一个版本,2012年发布1.0版本。在这10年的时间里,不断有开发者加入Golang的阵营中,不断共建Golang生态。其中比较有代表性的Golang编写软件作品是Docker和Kubernetes。从目前Golang的发展时间和社区活跃度来看,Golang无疑是一门成功的编程语言。

 

 

 

大道至简

有次微信公开课中,微信之父张小龙提到微信成功的秘诀就是简单,把所有功能做到最简单,UI最简单,用户使用最简单。这样使得竞品无法抄袭,因为微信是最简单的,对手加了一点东西都是冗余。那么什么使得Golang这么成功呢?

你可能会提到下面的原因:

  • 编译速度快
  • 执行速度快
  • 部署无依赖
  • 工具齐全
  • 官方库和第三方开源库丰富

但是这些不是语言特性,全都非常重要但是不是真正的答案。比较少谈起的是:接口或者并发这样真正的语言特性。真正的原因是简单。Golang是简单的,至少相比于当前的编程语言来说是简单的。简单有很多面,简单同时也是复杂的。语言使用上的简单对应的肯定是底层实现原理的复杂,和设计人员的精美设计的结果。

其他编程语言

Java,JavaScript,C#,C++,PHP,Python 这些编程语言积极的互相借鉴语言特性,例如这些语言都会有class(类)。它们正在汇总为一个简单的大型语言

编程语言需要不同的维度考虑:

逻辑编程(logic programming)

过程编程(procedural programming)

函数编程(functional programming)

面向对象编程(object-oriened programming)

并发编程(concurrent programming)

如果所有的编程语言都汇总在一起,那么我们思考的编程模式就会是一样的。但是有不同的思考方式是好的,需要不同的编程语言解决不同的问题。我们要的不是仅仅只有一个工具,而是一堆工具,不同的工具聚焦解决不同的问题。前面提到的语言,如Java8,C++14正在讨论新的特性,这些语言通过加入特性在不断的竞争。当某个语言出现了某个特性时,另外的语言一定会跟随,例如C++11/14加入的类型推导,不由得让人想起了脚本语言Python。这些语言在发展的过程会变得越来越复杂,同时也会变得越来越相似。它们会膨胀得没有区分度。

Golang 

Golang没有尝试像其他编程语言,不在特性上竞争。Golang 1.0版本发布时,这门语言的特性就已经固定了。很多Golang的新手会要求Golang具有一些他们知道的其他编程语言的特性。但是这些特性不属于Golang,这门编程语言的特性已经固定了。加入新特性不会使Golang变得更好,只会使它变得更大,更臃肿。

可读性

如果一门编程语言有太多特性,你会浪费时间在挑选用哪个的问题上。然后实现,精简,可能会回想和重做。

实际工作中,经常会发生的一幕是,有人会问,“为什么这些代码要这样写,这段代码是怎么工作的”,这个人可能是其他人,更有可能是你自己。这些代码是很难简单的能理解到,因为它用了一个更加复杂的编程语言,由于复杂编程语言的诸多特性,可能会导致很多分支的结果,所以我们需要分析这段代码的各种可能性,一不小心就会掉到坑里面。所以对于程序员来说,更倾向于只有一种实现方法,或者至少应该是尽量少的实现方法,更简单的实现方法。更多的特性会增加编程语言的复杂度,我们想要简单的编程语言。更多的特性会让可读性受损,我们想要可读性。可读性是最重要的。因为可读性意味着可靠性,如果你能够读懂代码和它意味着什么,就会很容易明白它的工作原理。如果它出现问题也会更容易修复。

设计理念

简单的交互方式是简单的特性。简单是Golang的目标。

Golang实际上实现原理非常复杂,但是它看起来很简单,使用很简单。语法非常简单和准确,没有歧义,没有惊喜。这需要Golang的创始人/团队经过大量的设计、实现和优化。所以简单是掩盖复杂的一门艺术。

下面会介绍Golang的几个简单特性:Gc机制(垃圾回收),goroutine,interface(接口),package(包)。每个简单的特性后面都是复杂的实现。

垃圾回收

垃圾回收可能是最好的用简单掩盖复杂的特性。实现起来非常难,但是很值得。因为有gc机制我们Golang代码编写得更简单。写代码的时候不需要关心“拥有者”。 

并发

Golang原生就支持了并发,而不是依赖第三方库。Golang并发包括下面三个元素:goroutines(执行体),channels(通信),select(协调)。

这里我们只需要暂时先关注goroutine,通过go function(args)启动一个独立运行的goroutine。三个字符’g’ ‘o’ ‘ ’就能启动一个goroutine。很难做到比这更简单的了。和gc机制一样,将编程人员需要关心的东西最小化。栈的大小,返回值和完成状态,线程 ID等都不用关心。虽然goroutine的实现很复杂,依赖于gc机制和栈管理,但是编程人员不用关心这些,只需要go就能启动一个goroutine。

接口

接口仅仅是方法的集合,没有数据。很简单的想法。

type Reader interface {

       Read([]byte)(int, error)

}

var r Reader = os.Stdin //静态检查

var x interface{} = os.Stdin //静态检查

r = x.(Reader) //动态检查,一定要是精准断言,否则会panic

在静态类型语言中加入动态类型转换,这些需要小心精准的设计。接口的赋值在运行时完成。最好是用“comma, ok”的语法,否则失败会panic。接口是Golang最有区分度和最强大的特性。深度影响了包的设计。另外包允许组合,例如可以将io.Reader和io.Writer组合为io.ReadWriter。感觉很简单,实则底层原理很复杂。

一个结构化程序和库的设计。

package big

…

import “math/big”

包的设计花费了Golang团队大量时间去设计。允许组合,可扩展,共享,数据隐藏(大小写),还有隔离 …。包的设计涉及到程序的设计,语法,命令,编译,链接,测试等等。但是对于Golang编程人员来说只需要使用package定义包,用import引用包即可。这也是用简单掩盖复杂的一个例子。

 

上述的示例都是为了说明Golang使用是非常简单,实现非常复杂。作为Golang的使用者,我们只需要清楚它的使用即可,而无需关心Golang的复杂实现,这也是Golang团队的目标。

 

下面是一个简单的Golang程序:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func 你好_Gophers(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "你好 Gophers!\n")
}

func main() {
    http.HandleFunc("/", 你好_Gophers)
    err := http.ListenAndServe("localhost:12345", nil)
    if err != nil {
        log.Fatal("ListenAndServer:", err)
    }
}

掩盖了下面的复杂过程:

Unicode和UTF8处理

包的引用和使用

Fprintf直接对接网络连接

函数作为参数传递(HandleFunc)

真正的并发 —server并没有阻塞

 

总结

Golang是使用简单,底层实现原理复杂的一门编程语言。

 

参考

https://www.youtube.com/watch?v=cQ7STILAS0M

 

posted @ 2019-07-16 18:05  我是码客  阅读(11567)  评论(4编辑  收藏  举报