Go語言必備技能——加快你的工做效率

簡介: 不管是Go語言小白,仍是有經驗的開發者,均可能從中獲取靈感。git

一句話技巧

把你面向對象的大腦扔到家裏吧,去擁抱接口。github

學習如何使用Go的方式作事,不要把別的的編程風格強行用在Go裏面。golang

多用接口總比少用好。面試

擁抱這種簡潔、並行、工整的語言。編程

閱讀官網golang.org上全部的文檔,真是棒呆了。網絡

別忘了用gofmt。數據結構

多讀源代碼。架構

學習工具和組件,而後創造你本身的!碼代碼和學代碼同樣對成功必不可少。運維

學而不思則罔,思而不學則殆。《論語》編程語言

引入package的多種方式

有幾種很是規方式來引入包(package)。接下來我會使用fmt來做爲例子:

import format "fmt" - 爲fmt創造一個別名。把代碼中全部使用到fmt的內容用format.代替fmt.

import . "fmt" - 容許包內的內容不加fmt前綴而被被直接引用

import _ "fmt" - 阻止編譯器爲引入fmt卻不使用裏面的內容作引起的警告,執行package中的初始化函數。提醒一句,在這種狀況下fmt是不可調用的

Goimports

命令goimports能夠更新您的Go導入行,添加缺乏的行,並刪除未引用的引導行。

它擁有和gofmt(插入式替換)相同的能力,可是goimports額外增長了修復imports的功能。

組織

Go是一種相對來講易學習的編程語言,但對於開發者來講,起初接觸這門語言最困難的事情就是如何組織代碼。scaffolding是人們喜歡Rails的緣由之一,它能夠給新晉的開發者清晰的方向,讓他們明白在哪裏插入代碼,應該遵循怎樣的編程風格。

做爲擴展,Go使用go fmt這樣的工具來提供開發者相同的功能。一樣地,Go的編譯器很是嚴格,它不會去編譯沒有使用的變量,或者沒有使用的import聲明。

自定義構造函數

我常常聽到別人問,「我何時應該使用像NewJob這樣的自定義構造函數?」,個人回答是「大多數情形下你不必這麼作」。然而,當你須要在初始化的時候就設置值,且你有一些默認值的時候,這就最好使用一個構造函數。在這個例子中,構造函數就比較有意義了,所以咱們用以下的代碼能夠構建一個默認的logger:

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...")
}

把代碼分解到不一樣的package中

以工程Gobot爲例,它能夠被分割爲一個核心package和一些其餘package。gobot的開發者們準備每一個部分放在本身的package裏。通過討論,他們選擇把全部的官方庫放在同一個repository下,讓import路徑變得乾淨而富有邏輯。

因此,他們不打算把路徑設置爲:

github.com/hybridgroup/gobot
github.com/hybridgroup/gobot-sphero
github.com/hybridgroup/gobot-...

而是設置爲

github.com/hybridgroup/gobot
github.com/hybridgroup/gobot/sphero
github.com/hybridgroup/gobot/...

如今package的名字再也不是冗長的gobot-sphero,而變成了簡要的sphero。

集合(Sets)

在其餘的程序語言中,常常會有一種數據結構叫作sets,它容許把元素存入,可是不容許重複。Go並不直接支持這種結構,可是這個結構在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
}

Playground連接

在這裏,我會使用一些很是有意思的花招。首先,對空結構的映射:

m := map[string]struct{}{}

咱們建立了一個map,這能夠確保key是獨一無二的,而相關聯的value實際上是咱們不關心的。
咱們固然可使用:

m := map[string]bool{}

可是,使用空結構體能夠達到一樣的效率,同時不會佔用額外的內存。

第二個花招的意味更爲深遠:

if _, ok := m[v]; !ok {
  m[v] = struct{}{}
}

這裏作的事情就是確認map m中的某個值是否存在,而不關心value自己。若是發現沒有對應的值,就去加一個。固然,不去驗證直接加好像也沒有什麼區別。

一旦咱們擁有了一個充滿獨一無二key的map之後,就能夠把他們放到一個切片裏,返回結果了。

這裏有一段測試代碼,正如你所見,這裏使用了一個符合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)
        }
    }
}

通過測試發現,並不是每次都可以成功,而是有機率的。由於map是使用hashmap實現的,使用range進行遍歷的時候,其遍歷順序和字符串的內容沒有必然聯繫,所以此test有可能失敗。在進行DeapEqual比對的時候,可能會爆出相似於["b" "c" "a"] didn't match ["a" "b" "c"]的錯誤。固然,在Playground中,每次執行的上下文環境如出一轍,所以這裏的test是總能經過的。

依賴包管理

很遺憾,Go語言官方並不提供依賴包管理系統。這極可能是由於go語言植根於C語言的文化,所以它沒有辦法引入特定版本的包。

這會帶來一些嚴重的問題:

1.當多個開發者共同維護一個項目時,不一樣開發者的依賴版本可能不一樣。
2.依賴也會有他們自身的依賴,因此很難確保全部的依賴都使用同一個版本。
3.你的多個項目基於了同一個依賴的不一樣版本。

對於最後一種情形,能夠經過搭建一個_持續集成環境(Continuousintegration)來解決,可是前二者就相對困難。

※部分文章來源於網絡,若有侵權請聯繫刪除;更多文章和資料|點擊後方文字直達 ↓↓↓
100GPython自學資料包
阿里雲K8s實戰手冊
[阿里雲CDN排坑指南] CDN
ECS運維指南
DevOps實踐手冊
Hadoop大數據實戰手冊
Knative雲原生應用開發指南
OSS 運維實戰手冊
雲原生架構白皮書
Zabbix企業級分佈式監控系統源碼文檔
10G大廠面試題戳領
相關文章
相關標籤/搜索