Golang 受歡迎的原因:大道至簡

我是碼客發表於2019-07-16

前言

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

 

相關文章