Go語言技術(shù)特點探究
1?【引子】
前段時間軟件學(xué)院的劉杰珍(340654)給軟件教練們作軟件可信認(rèn)證的相關(guān)介紹時,提到了目前的可信認(rèn)證包括如下的內(nèi)容:
設(shè)計模式,重構(gòu),面向?qū)ο笤O(shè)計,編程語言高階特性,其中編程語言包括C, C++, Java, Go, Python, Javascript。
針對以上內(nèi)容,目前已經(jīng)做了:
ü??C語言技術(shù)特點的總結(jié)《C語言的技術(shù)特點探究》
ü??代碼重構(gòu)技術(shù)的總結(jié)《代碼重構(gòu)技術(shù)探究》
本文我們一起來探討和學(xué)習(xí)一下Go語言的技術(shù)特點。先來看個例子:
package?main
import?(
"fmt"
"time"
)
//?要在goroutine中運行的函數(shù)。done通道將被用來通知工作已經(jīng)完成。
func?worker(done?chan?bool)?{
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
//?通知完成。
done?<-?true
}
func?main()?{
//?創(chuàng)建一個通道
done?:=?make(chan?bool,?1)
go?worker(done)
//?等待done變?yōu)閠rue
<-done
}
上例中是一個在Go語言中使用goroutine和通道的例子。 其中:
n??go?關(guān)鍵字是用來啟動一個goroutine
n??done <- true,?向通道傳值
n??<-done, 讀取通道值
2?【概述】
Go是由RobertGriesemer、RobPike和KenThompson在Google設(shè)計的一種靜態(tài)類型化的、須編譯后才能運行的編程語言。
Go在語法上類似于C語言,但它具有C語言沒有的優(yōu)勢,如內(nèi)存安全、垃圾回收、結(jié)構(gòu)化的類型和CSP風(fēng)格的并發(fā)性。
它的域名是golang.org,所以通常被稱為"Golang",但正確的名稱是Go。
3?【GO的設(shè)計思想】
Go的設(shè)計受C語言的影響,但更加簡單和安全。該語言包括如下特點:
ü??采用動態(tài)語言中比較常見的語法和環(huán)境模式:
ü??可選的簡明變量聲明和通過類型推理進(jìn)行初始化(如果使用x := 0而不是int x= 0;或var x= 0;)。
ü??快速編譯。
ü??遠(yuǎn)程包管理(go get)和在線包文檔。
ü??針對特定問題的獨特方法:
ü??內(nèi)置的并發(fā)基元:輕量級處理機制(goroutines)、通道和select語句。
ü??用接口系統(tǒng)代替虛擬繼承,用類型嵌入代替非虛擬繼承。
ü??默認(rèn)情況下,由一個工具鏈生成靜態(tài)鏈接的原生二進(jìn)制文件,沒有外部依賴關(guān)系。
ü??希望保持語言規(guī)范足夠簡單,程序員容易掌握。
3.1????簡潔的語法
Go的語法包含C語言中保持代碼簡潔性和可讀性的語法特點。
3.1.1????變量聲明
引入了一個聯(lián)合聲明/初始化操作符,允許程序員寫出i := 3或s :="Hello, world!",而不需要指定使用的變量類型。
這與C語言中的int i= 3;?和?const char?*s = "Hello, world!";形成鮮明對比。
3.1.2????分號隱含
分號仍然是終止語句,但在行結(jié)束時是隱含的。
3.1.3????返回多值
在Go中,一個函數(shù)方法可以返回多個值,返回一個結(jié)果和錯誤err組合對是向調(diào)用者提示錯誤的常規(guī)方式。
3.1.4????范圍表達(dá)式
Go的范圍表達(dá)式允許在數(shù)組、動態(tài)數(shù)組、字符串、字典和通道上進(jìn)行簡潔的迭代,在C語言中,有三種循環(huán)來實現(xiàn)這個功能。
3.2????類型系統(tǒng)
3.2.1????內(nèi)置的類型
Go有許多內(nèi)置的類型,包括數(shù)字類型(byte、int64、float32等)、booleans和字符串(string)。
字符串是不可更改的。
內(nèi)置的運算符和關(guān)鍵字(而不是函數(shù))提供了串聯(lián)、比較和UTF-8編碼/解碼。
3.2.2????結(jié)構(gòu)類型
記錄類型可以用struct關(guān)鍵字定義。
3.2.3????數(shù)組類型
對于每個類型T和每個非負(fù)整數(shù)常數(shù)n,都有一個數(shù)組類型,表示為[n]T,因此,不同長度的數(shù)組有不同的類型。
動態(tài)數(shù)組可以作為"Slice"使用,如對于某類型T,表示為[]T。這些數(shù)組有一個長度和一個容量,容量規(guī)定了何時需要分配新的內(nèi)存來擴(kuò)展數(shù)組。若干個Slice可以共享它們的底層內(nèi)存。
3.2.4????指針
所有類型都可以定義指針,?T類型的指針可定義為*T。地址抽取和隱式訪問使用&和*操作符,這跟C語言一樣,或者隱式的通過方法調(diào)用或?qū)傩栽L問使用。
除了標(biāo)準(zhǔn)庫中的特殊的unsafe.Pointer類型,一般指針沒有指針運算。
3.2.5????映射類型
對于一個組合對類型K、V,類型map[K]V是將類型K鍵映射到類型V值的哈希表的類型。
3.2.6????通道類型
chan T是一個通道,允許在并發(fā)的Go進(jìn)程之間發(fā)送T類型的值。
3.2.7????顯式類型
除了對接口的支持外,Go的類型系統(tǒng)是顯示的:類型關(guān)鍵字可以用來定義一個新的命名類型,它與其他具有相同布局的命名類型(對于結(jié)構(gòu)體來說,相同的成員按相同的順序排列)不同。類型之間的一些轉(zhuǎn)換(如各種整數(shù)類型之間的轉(zhuǎn)換)是預(yù)先定義好的,添加一個新的類型可以定義額外的轉(zhuǎn)換,但命名類型之間的轉(zhuǎn)換必須始終顯式調(diào)用。例如,類型關(guān)鍵字可以用來定義IPv4地址的類型,基于32位無符號整數(shù):
type?ipv4addr?uint32
通過這個類型定義,ipv4addr(x)將uint32值x解釋為IP地址。如果簡單地將x分配給類型為ipv4addr的變量將會是一個類型錯誤。
常量表達(dá)式既可以是類型化的,也可以是?"非類型化的";如果它們所代表的值通過了編譯時的檢查,那么當(dāng)它們被分配給一個類型化的變量時,就會被賦予一個類型。
3.2.8????函數(shù)類型
函數(shù)類型由func關(guān)鍵字表示;它們?nèi)?個或更多的參數(shù)并返回0個或更多的值,這些值都是要聲明類型的。
參數(shù)和返回值決定了一個函數(shù)的類型;比如,func(string, int32)(int, error)就是輸入一個字符串和一個32位有符號的整數(shù),并返回一個有符號的整數(shù)和一個錯誤(內(nèi)置接口類型)的值的函數(shù)類型。
3.2.9????類型上的方法擴(kuò)展
任何命名的類型都有一個與之相關(guān)聯(lián)的方法集合。上面的IP地址例子可以用一個檢查其值是否為已知標(biāo)準(zhǔn)的方法來擴(kuò)展:
//?ZeroBroadcast報告addr是否為255.255.255.255.255。
func?(addr?ipv4addr)?ZeroBroadcast()?bool?{
return?addr?==?0xFFFFFFFF
}
以上的函數(shù)在ipv4addr上增加了一個方法,但這個方法在uint32上沒有。
3.2.10?接口系統(tǒng)
Go提供了兩個功能來取代類繼承。
首先是嵌入方法,可以看成是一種自動化的構(gòu)成形式或委托代理。
第二種是接口,它提供了運行時的多態(tài)性。
接口是一類型,它在Go的類型系統(tǒng)中提供了一種有限的結(jié)構(gòu)類型化形式。
一個接口類型的對象同時也有另一種類型的定義對應(yīng),這點就像C++對象同時具有基類和派生類的特征一樣。
Go接口是在Smalltalk編程語言的協(xié)議基礎(chǔ)上設(shè)計的。
在描述Go接口時使用了鴨式填充這個術(shù)語。
雖然鴨式填充這個術(shù)語沒有精確的定義,它通常是說這些對象的類型一致性沒有被靜態(tài)檢查。
接口類型的定義按名稱和類型列出了所需的方法。任何存在與接口類型I的所需方法匹配的函數(shù)的T類型的對象也是類型I的對象。類型T的定義不需要也不能識別類型I。例如,如果Shape、Square和Circle被定義為:
import?"math"
type?Shape?interface?{
Area()?float64
}
type?Square?struct?{?//?注:沒有?"實現(xiàn)?"聲明
side?float64
}
func?(sq?Square)?Area()?float64?{?return?sq.side?*?sq.side?}
type?Circle?struct?{?//?這里也沒有?"實現(xiàn)?"聲明
radius?float64
}
func?(c?Circle)?Area()?float64?{?return?math.Pi?*?math.Pow(c.radius,?2)?}
一個正方形和一個圓都隱含著一個形狀(Shape)類型,并且可以被分配給一個形狀(Shape)類型的變量。
Go的接口系統(tǒng)使用了了結(jié)構(gòu)類型。接口也可以嵌入其他接口,其效果是創(chuàng)建一個組合接口,而這個組合接口正是由實現(xiàn)嵌入接口的類型和新定義的接口所增加的方法來滿足的。
Go標(biāo)準(zhǔn)庫在多個地方使用接口來提供通用性,這包括基于Reader和Writer概念的輸入輸出系統(tǒng)。
除了通過接口調(diào)用方法,Go還允許通過運行時類型檢查將接口值轉(zhuǎn)換為其他類型。這就是類型斷言和類型切換。
空接口{}是一個重要的基本情況,因為它可以引用任何類型的選項。它類似于Java或C#中的Object類,可以滿足任何類型,包括像int這樣的內(nèi)置類型。
使用空接口的代碼不能簡單地在被引用的對象上調(diào)用方法或內(nèi)置操作符,但它可以存儲interface{}值,通過類型斷言或類型切換嘗試將其轉(zhuǎn)換為更有用的類型,或者用Go的reflect包來檢查它。
因為?interface{}?可以引用任何值,所以它是一種擺脫靜態(tài)類型化限制的有效方式,就像C?語言中的?void*,但在運行時會有額外的類型檢查。
接口值是使用指向數(shù)據(jù)的指針和第二個指向運行時類型信息的指針來實現(xiàn)的。與Go中其他一些使用指針實現(xiàn)的類型一樣,如果未初始化,接口值是零。
3.3????程序包系統(tǒng)
在Go的包系統(tǒng)中,每個包都有一個路徑(如"compress/bzip2 "或"golang.org/x/net/html")和一個名稱(如bzip2或html)。
對其他包的定義的引用必須始終以其他包的名稱作為前綴,并且只有其他包的大寫的名稱才能被訪問:io.Reader是公開的,但bzip2.reader不是。
go get命令可以檢索存儲在遠(yuǎn)程資源庫中的包,鼓勵開發(fā)者在開發(fā)包時,在與源資源庫相對應(yīng)的基礎(chǔ)路徑
(如example.com/user_name/package_name)內(nèi)開發(fā)程序包,從而減少將來在標(biāo)準(zhǔn)庫或其他外部庫中名稱碰撞的可能性。
有人提議Go引入一個合適的包管理解決方案,類似于CPANfor Perl或Rust的Cargo系統(tǒng)或Node的npm系統(tǒng)。
3.4????并發(fā):goroutines和通道
3.4.1????【CSP并發(fā)模式】
在計算機科學(xué)中,通信順序過程(communicating sequential processes,CSP)是一種描述并發(fā)系統(tǒng)中交互模式的正式語言,它是并發(fā)數(shù)學(xué)理論家族中的一個成員,被稱為過程算法(process algebras),或者說過程計算(process calculate),是基于消息的通道傳遞的數(shù)學(xué)理論。
CSP在設(shè)計Oceam編程語言時起了很大的影響,同時也影響了Limbo、RaftLib、Go、Crystal和Clojure的core.async等編程語言的設(shè)計。
CSP最早是由TonyHoare在1978年的一篇論文中描述的,后來有了很大的發(fā)展。
CSP作為一種工具被實際應(yīng)用于工業(yè)上,用于指定和驗證各種不同系統(tǒng)的并發(fā)功能,如T9000Transputer以及安全的電子商務(wù)系統(tǒng)。
CSP本身的理論目前也仍然是被積極研究的對象,包括增加其實際適用范圍的工作,如增加可分析的系統(tǒng)規(guī)模。
Go語言有內(nèi)置的機制和庫支持來編寫并發(fā)程序。并發(fā)不僅指的是CPU的并行性,還指的是異步性處理:讓相對慢的操作,如數(shù)據(jù)庫或網(wǎng)絡(luò)讀取等操作在做其他工作的同時運行,這在基于事件的服務(wù)器中很常見。
主要的并發(fā)構(gòu)造是goroutine,這是一種輕量級處理類型。一個以go關(guān)鍵字為前綴的函數(shù)調(diào)用會在一個新的goroutine中啟動這個函數(shù)。
語言規(guī)范并沒有指定如何實現(xiàn)goroutine,但目前的實現(xiàn)將Go進(jìn)程的goroutine復(fù)用到一個較小的操作系統(tǒng)線程集上,類似于Erlang中的調(diào)度。
雖然一個標(biāo)準(zhǔn)的庫包具有大多數(shù)經(jīng)典的并發(fā)控制結(jié)構(gòu)(mutex鎖等),但Go并發(fā)程序更偏重于通道,它提供了goroutines之間的消息傳功能。
可選的緩沖區(qū)以FIFO順序存儲消息,允許發(fā)送的goroutines在收到消息之前繼續(xù)進(jìn)行。
通道是類型化的,所以chan T類型的通道只能用于傳輸T類型的消息。
特殊語法約定用于對它們進(jìn)行操作;<-ch是一個表達(dá)式,它使執(zhí)行中的goroutine在通道ch上阻塞,直到有一個值進(jìn)來,而ch<- x則是發(fā)送值x(可能阻塞直到另一個goroutine接收到這個值)。
內(nèi)置的類似于開關(guān)的選擇語句可以用來實現(xiàn)多通道上的非阻塞通信。Go有一個內(nèi)存模型,描述了goroutine必須如何使用通道或其他操作來安全地共享數(shù)據(jù)。
通道的存在使Go有別于像Erlang這樣的actor模型式的并發(fā)語言,在這種語言中,消息是直接面向actor(對應(yīng)于goroutine)的。在Go中,可以通過在goroutine和通道之間保持一對一的對應(yīng)關(guān)系來,Go語言也允許多個goroutine共享一個通道,或者一個goroutine在多個通道上發(fā)送和接收消息。
通過這些功能,人們可以構(gòu)建像workerpools、流水線(比如說,在下載文件時,對文件進(jìn)行解壓縮和解析)、帶超時的后臺調(diào)用、對一組服務(wù)的"扇出"并行調(diào)用等并發(fā)構(gòu)造。
通道也有一些超越進(jìn)程間通信的常規(guī)概念的用途,比如作為一個并發(fā)安全的回收緩沖區(qū)列表,實現(xiàn)coroutines和實現(xiàn)迭代器。
Go的并發(fā)相關(guān)的結(jié)構(gòu)約定(通道和替代通道輸入)來自于TonyHoare的通信順序進(jìn)程模型。
不像以前的并發(fā)編程語言,如Occam或Limbo(Go的共同設(shè)計者RobPike曾在此基礎(chǔ)上工作過的語言),Go沒有提供任何內(nèi)置的安全或可驗證的并發(fā)概念。
雖然在Go中,上述的通信處理模型是推薦使用的,但不是唯一的:一個程序中的所有g(shù)oroutines共享一個單一的地址空間。這意味著可突變對象和指針可以在goroutines之間共享。
3.5????并行編程的舒適度
有一項研究比較了一個不熟悉Go語言的老練程序員編寫的程序的大小(以代碼行數(shù)為單位)和速度,以及一個Go專家(來自Google開發(fā)團(tuán)隊)對這些程序的修正,對Chapel、Cilk和IntelTBB做了同樣的研究。
研究發(fā)現(xiàn),非專家傾向于用每個遞歸中的一條Go語句來寫分解-解決算法,而專家則用每個處理器的一條Go語句來寫分布式工作同步程序。Go專家的程序通常更快,但也更長。
3.6????條件競賽安全問題
Goroutine對于如何訪問共享數(shù)據(jù)沒有限制,這使得條件競賽成為可能的問題。
具體來說,除非程序通過通道或其他方式顯式同步,否則多個goroutine共享讀寫一個內(nèi)存區(qū)域可能會發(fā)生問題。
此外,Go的內(nèi)部數(shù)據(jù)結(jié)構(gòu),如接口值、動態(tài)數(shù)組頭、哈希表和字符串頭等內(nèi)部數(shù)據(jù)結(jié)構(gòu)也不能幸免于條件競賽,因此在多線程程序中,如果修改這些類型的共享實例沒有同步,就會存在影響類型和內(nèi)存安全的情況。
3.7????二進(jìn)制生成
gc工具鏈中的鏈接器默認(rèn)會創(chuàng)建靜態(tài)鏈接的二進(jìn)制文件,因此所有的Go二進(jìn)制文件都包括Go運行所需要的內(nèi)容。
3.8????舍棄的語言特征
Go故意省略了其他語言中常見的一些功能,包括繼承、通用編程、斷言、指針運算、隱式類型轉(zhuǎn)換、無標(biāo)記的聯(lián)合和標(biāo)記聯(lián)合。
3.9????Go風(fēng)格特點
·?????????gofmt工具自動規(guī)范了代碼的縮進(jìn)、間距和其他表面級的細(xì)節(jié)。
·?????????與Go一起分發(fā)的工具和庫推薦了一些標(biāo)準(zhǔn)的方法,比如API文檔(godoc)、 測試(go test)、構(gòu)建(go build)、包管理(go get)等等。
·?????????Go的一些規(guī)則跟其他語言不同,例如禁止循環(huán)依賴、未使用的變量或?qū)搿㈦[式類型轉(zhuǎn)換等。
·?????????某些特性的省略(例如,函數(shù)編程的一些捷徑,如map和Java風(fēng)格的try/finally塊)編程風(fēng)格顯式化,具體化,簡單化。
·?????????Go團(tuán)隊從第一天開始就發(fā)布了一個Go的語法使用集合,后來還收集了一些代碼的評論,講座和官方博客文章,來推廣Go的風(fēng)格和編碼理念。
4? ?Go的工具
主要的Go發(fā)行版包括構(gòu)建、測試和分析代碼的工具。
ü??go build,它只使用源文件中的信息來構(gòu)建Go二進(jìn)制文件,不使用單獨的makefiles。
ü??gotest,用于單元測試和微基準(zhǔn)
ü??go fmt,用于格式化代碼
ü??go get,用于檢索和安裝遠(yuǎn)程包。
ü??go vet,靜態(tài)分析器,查找代碼中的潛在錯誤。
ü??go run,構(gòu)建和執(zhí)行代碼的快捷方式
ü??godoc,用于顯示文檔或通過HTTP
ü??gorename,用于以類型安全的方式重命名變量、函數(shù)等。
ü??go generate,一個標(biāo)準(zhǔn)的調(diào)用代碼生成器的方法。
它還包括分析和調(diào)試支持、運行時診斷(例如,跟蹤垃圾收集暫停)和條件競賽測試器。
第三方工具的生態(tài)系統(tǒng)增強了標(biāo)準(zhǔn)的發(fā)布系統(tǒng),如:
gocode,它可以在許多文本編輯器中自動完成代碼,
goimports(由Go團(tuán)隊成員提供),它可以根據(jù)需要自動添加/刪除包導(dǎo)入,以及errcheck,它可以檢測可能無意中被忽略的錯誤代碼。
5???編輯環(huán)境
流行的Go代碼工具:
ü??GoLand:JetBrains公司的IDE。
ü??VisualStudio Code
ü??LiteIDE:一個"簡單、開源、跨平臺的GoIDE"
ü??Vim:用戶可以安裝插件:
ü??vim-go
6? ?應(yīng)用案例
用Go編寫的一些著名的開源應(yīng)用包括:
ü??Caddy,一個開源的HTTP/2web服務(wù)器,具有自動HTTPS功能。
ü??CockroachDB,一個開源的、可生存的、強一致性、可擴(kuò)展的SQL數(shù)據(jù)庫。
ü??Docker,一套用于部署Linux容器的工具。
ü??Ethereum,以太幣虛擬機區(qū)塊鏈的Go-Ethereum實現(xiàn)。
ü??Hugo,一個靜態(tài)網(wǎng)站生成器
ü??InfluxDB,一個專門用于處理高可用性和高性能要求的時間序列數(shù)據(jù)的開源數(shù)據(jù)庫。
ü??InterPlanetaryFile System,一個可內(nèi)容尋址、點對點的超媒體協(xié)議。
ü??Juju,由UbuntuLinux的包裝商Canonical公司推出的服務(wù)協(xié)調(diào)工具。
ü??Kubernetes容器管理系統(tǒng)
ü??lnd,比特幣閃電網(wǎng)絡(luò)的實現(xiàn)。
ü??Mattermost,一個團(tuán)隊聊天系統(tǒng)
ü??NATSMessaging,是一個開源的消息傳遞系統(tǒng),其核心設(shè)計原則是性能、可擴(kuò)展性和易用性。
ü??OpenShift,云計算服務(wù)平臺
ü??Snappy,一個由Canonical開發(fā)的UbuntuTouch軟件包管理器。
ü??Syncthing,一個開源的文件同步客戶端/服務(wù)器應(yīng)用程序。
ü??Terraform,是HashiCorp公司的一款開源的多云基礎(chǔ)設(shè)施配置工具。
ü??其他使用Go的知名公司和網(wǎng)站包括:
ü??Cacoo,使用Go和gRPC渲染用戶儀表板頁面和微服務(wù)。
ü??Chango,程序化廣告公司,在其實時競價系統(tǒng)中使用Go。
ü??CloudFoundry,平臺即服務(wù)系統(tǒng)
ü??Cloudflare,三角編碼代理Railgun,分布式DNS服務(wù),以及密碼學(xué)、日志、流處理和訪問SPDY網(wǎng)站的工具。
ü??容器Linux(原CoreOS),是一個基于Linux的操作系統(tǒng),使用Docker容器和rkt容器。
ü??Couchbase、Couchbase服務(wù)器內(nèi)的查詢和索引服務(wù)。
ü??Dropbox,將部分關(guān)鍵組件從Python遷移到了Go。
ü??谷歌,許多項目,特別是下載服務(wù)器dl.google.com。
ü??Heroku,Doozer,一個提供鎖具服務(wù)的公司
ü??HyperledgerFabric,一個開源的企業(yè)級分布式分類賬項目。
ü??MongoDB,管理MongoDB實例的工具。
ü??Netflix的服務(wù)器架構(gòu)的兩個部分。
ü??Nutanix,用于其企業(yè)云操作系統(tǒng)中的各種微服務(wù)。
ü??Plug.dj,一個互動式在線社交音樂流媒體網(wǎng)站。
ü??SendGrid是一家位于科羅拉多州博爾德市的事務(wù)性電子郵件發(fā)送和管理服務(wù)。
ü??SoundCloud,"幾十個系統(tǒng)"
ü??Splice,其在線音樂協(xié)作平臺的整個后端(API和解析器)。
ü??ThoughtWorks,持續(xù)傳遞和即時信息的工具和應(yīng)用(CoyIM)。
ü??Twitch,他們基于IRC的聊天系統(tǒng)(從Python移植過來的)。
ü??Uber,處理大量基于地理信息的查詢。
7??代碼示例
7.1????Hello World
package?main
import?"fmt"
func?main()?{
fmt.Println("Hello,?world!")
}
7.2????并發(fā)
package?main
import?(
"fmt"
"time"
)
func?readword(ch?chan?string)?{
fmt.Println("Type?a?word,?then?hit?Enter.")
var?word?string
fmt.Scanf("%s",?&word)
ch?<-?word
}
func?timeout(t?chan?bool)?{
time.Sleep(5?*?time.Second)
t?<-?false
}
func?main()?{
t?:=?make(chan?bool)
go?timeout(t)
ch?:=?make(chan?string)
go?readword(ch)
select?{
case?word?:=?<-ch:
fmt.Println("Received",?word)
case?<-t:
fmt.Println("Timeout.")
}
}
7.3????代碼測試
沒有測試的代碼是不完整的,因此我們需要看看代碼測試部分的編寫。
代碼:
func?ExtractUsername(email?string)?string?{
at?:=?strings.Index(email,?"@")
return?email[:at]
}
測試案例:
func?TestExtractUsername(t?*testing.T)?{
type?args?struct?{
email?string
}
tests?:=?[]struct?{
name?string
args?args
want?string
}{
{"withoutDot",?args{email:?"r@google.com"},?"r"},
{"withDot",?args{email:?"jonh.smith@example.com"},?"jonh.smith"},
}
for?_,?tt?:=?range?tests?{
t.Run(tt.name,?func(t?*testing.T)?{
if?got?:=?ExtractUsername(tt.args.email);?got?!=?tt.want?{
t.Errorf("ExtractUsername()?=?%v,?want?%v",?got,?tt.want)
}
})
}
}
7.4????創(chuàng)建后端服務(wù)
接下來我寫一個例子創(chuàng)建REST API后端服務(wù):
我們的服務(wù)提供如下的API:
###
GET?http://localhost:10000/
###
GET?http://localhost:10000/all
###
GET?http://localhost:10000/article/1
###
POST?http://localhost:10000/article?HTTP/1.1
{
"Id":?"3",
"Title":?"Hello?2",
"desc":?"Article?Description",
"content":?"Article?Content"
}
###
PUT?http://localhost:10000/article?HTTP/1.1
{
"Id":?"2",
"Title":?"Hello?2?Update",
"desc":?"Article?Description?Update",
"content":?"Article?Content?Update"
}
完整代碼:
package?main
import?(
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/gorilla/mux"
)
type?Article?struct?{
Id??????string?`json:"Id"`
Title???string?`json:"Title"`
Desc????string?`json:"desc"`
Content?string?`json:"content"`
}
var?MapArticles?map[string]Article
var?Articles?[]Article
func?returnAllArticles(w?http.ResponseWriter,?r?*http.Request)?{
fmt.Println("Endpoint?Hit:?returnAllArticles")
json.NewEncoder(w).Encode(Articles)
}
func?homePage(w?http.ResponseWriter,?r?*http.Request)?{
fmt.Fprintf(w,?"Welcome?to?the?HomePage!")
fmt.Println("Endpoint?Hit:?homePage")
}
func?createNewArticle(w?http.ResponseWriter,?r?*http.Request)?{
reqBody,?_?:=?ioutil.ReadAll(r.Body)
var?article?Article
json.Unmarshal(reqBody,?&article)
Articles?=?append(Articles,?article)
MapArticles[article.Id]?=?article
json.NewEncoder(w).Encode(article)
}
func?updateArticle(w?http.ResponseWriter,?r?*http.Request)?{
reqBody,?_?:=?ioutil.ReadAll(r.Body)
var?article?Article
json.Unmarshal(reqBody,?&article)
found?:=?false
for?index,?v?:=?range?Articles?{
if?v.Id?==?article.Id?{
//?Found!
found?=?true
Articles[index]?=?article
}
}
if?!found?{
Articles?=?append(Articles,?article)
}
MapArticles[article.Id]?=?article
json.NewEncoder(w).Encode(article)
}
func?returnSingleArticle(w?http.ResponseWriter,?r?*http.Request)?{
vars?:=?mux.Vars(r)
key?:=?vars["id"]
fmt.Fprintf(w,?"Key:?%s?\n",?key)
json.NewEncoder(w).Encode(MapArticles[key])
}
func?handleRequests()?{
myRouter?:=?mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/",?homePage)
myRouter.HandleFunc("/all",?returnAllArticles)
myRouter.HandleFunc("/article",?createNewArticle).Methods("POST")
myRouter.HandleFunc("/article",?updateArticle).Methods("PUT")
myRouter.HandleFunc("/article/{id}",?returnSingleArticle)
log.Fatal(http.ListenAndServe(":10000",?myRouter))
}
func?main()?{
fmt.Println("Rest?API?is?ready?...")
MapArticles?=?make(map[string]Article)
Articles?=?[]Article{
Article{Id:?"1",?Title:?"Hello",?Desc:?"Article?Description",?Content:?"Article?Content"},
Article{Id:?"2",?Title:?"Hello?2",?Desc:?"Article?Description",?Content:?"Article?Content"},
}
for?_,?a?:=?range?Articles?{
MapArticles[a.Id]?=?a
}
handleRequests()
}
調(diào)用添加,更新API以后返回所有數(shù)據(jù)的測試結(jié)果:
8???褒貶不一
8.1????贊揚
MicheleSimionato對Go大加贊揚:
ü??接口系統(tǒng)簡潔,并刻意省略了繼承。
EngineYard的DaveAstels寫道:
ü??Go是非常容易上手的。很少的基本語言概念,語法也很干凈,設(shè)計得很清晰。Go目前還是實驗性的,還有些地方比較粗糙。
2009年,Go被TIOBE編程社區(qū)指數(shù)評選為年度最佳編程語言。
到2010年1月,Go的排名達(dá)到了第13位,超過了Pascal等成熟的語言。
但到了2015年6月,它的排名跌至第50位以下,低于COBOL和Fortran。
但截至2017年1月,它的排名又飆升至第13位,顯示出它的普及率和采用率有了顯著的增長。
Go被評為2016年TIOBE年度最佳編程語言。
BruceEckel曾表示:
ü??C++的復(fù)雜性(在新的C++中甚至增加了更多的復(fù)雜性),以及由此帶來的對生產(chǎn)力的影響,已經(jīng)沒有任何理由繼續(xù)使用C++了。C++程序員為了克服C語言的一些問題而做出的增強初衷目前已經(jīng)沒有了意義,而Go此時顯得更有意義。
2011年一位Google工程師R.Hundt對Go語言及其GC實現(xiàn)與C++(GCC)、Java和Scala的對比評估發(fā)現(xiàn)。
ü??Go提供了有趣的語言特性,這也使得Go語言有了簡潔、標(biāo)準(zhǔn)化的特征。這種語言的編譯器還不成熟,這在性能和二進(jìn)制大小上都有體現(xiàn)。
這一評價收到了Go開發(fā)團(tuán)隊的快速反應(yīng)。
IanLance Taylor因為Hundt的評論改進(jìn)了Go代碼;
RussCox隨后對Go代碼以及C++代碼進(jìn)行了優(yōu)化,并讓Go代碼的運行速度比C++略快,比評論中使用的代碼性能快了一個數(shù)量級以上。
8.2????命名爭議
2009年11月10日,也就是Go!編程語言全面發(fā)布的當(dāng)天,Go!編程語言的開發(fā)者FrancisMcCabe(注意是感嘆號)要求更改Google的語言名稱,以避免與他花了10年時間開發(fā)的語言混淆。
McCabe表示了對谷歌這個'大塊頭'最終會碾壓他"的擔(dān)憂,這種擔(dān)憂引起了120多名開發(fā)者的共鳴,他們在Google官方的問題線程上評論說他們應(yīng)該改名,有些人甚至說這個問題違背了Google的座右銘:"不要作惡。"
2010年10月12日,谷歌開發(fā)者RussCox關(guān)閉了這個問題,自定義狀態(tài)為"不幸",并附上了以下評論:
"有很多計算產(chǎn)品和服務(wù)都被命名為Go。在我們發(fā)布以來的11個月里,這兩種語言的混淆度極低。"
8.3????【批評】
Go的批評家們的觀點:
ü??通用程序設(shè)計缺乏參數(shù)多態(tài)性,導(dǎo)致代碼重復(fù)或不安全的類型轉(zhuǎn)換以及擾亂流程的隨意性。
ü??Go的nil,加上代數(shù)類型的缺失,導(dǎo)致故障處理和基本問題的避免很困難。
ü??Go不允許在當(dāng)前行中出現(xiàn)開局括號,這就迫使所有的Go程序員必須使用相同的括號樣式。
9? ?【小結(jié)】
本文從Go的語法,類型系統(tǒng),編碼風(fēng)格,語言工具,編碼工具和使用案例等幾方面對Go語言進(jìn)行了學(xué)習(xí)和探討,希望可以拋磚引玉,對Go語言感興趣的同仁有所裨益。
歡迎討論。
10?【更多文章】
https://en.wikipedia.org/wiki/Go_(programming_language)
Jet Ding文章歸類索引表
軟件開發(fā) Go 代碼檢查 CodeCheck
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。