【GoLang】golang 最佳實踐匯總


 

最佳實踐
1 包管理
1.1 使用包管理對Golang項目進行管理,如:godep/vendor等工具
1.2 main/init函數使用,init函數參考python
1.2.1 main->import->const->var->init
1.2.2 同一個package屬於一個作用域,所以不要重復定義變量等
1.3 萬能的type
1.3.1 type ages int type money float32 type months map[string]int 定義新類型
1.3.2 type testInt func(int) (bool) // 聲明了一個函數類型
1.3.3 type person struct //聲明一個結構體類型
1.3.4 type PeopleProtocol interface //聲明一個接口類型
1.4 萬能的函數
1.4.1 面向函數編程
1.4.1.1 數據作為參數傳遞給函數形參
1.4.2 面向對象編程
1.4.2.1 通過帶有接受者函數實現,使用起來函數作為了對象的屬性
1.4.2.2 golang可以自動匹配接受者的類型,不管是對象還是對象的指針
1.4.2.3 字段繼承通過匿名字段實現;方法繼承/重寫也通過匿名字段實現
1.4.3 面向接口編程
1.4.3.1 接口定義協議,通過帶接受者的函數實現接口,實現了接口的對象可以被接口引用
1.4.3.2 面向接口不是對面向對象的否定,它是面向對象編程體系中的思想精髓之一
1.4.3.3 面向接口編程的本質是面向協議編程,只要實現了該協議,不管什么對象都是可以隨時替代的,可以提高軟件的靈活性與可維護性opic
1.4.3.4 面向對象是為了實現代碼復用,面向接口是為了實現多態、實現標准定制、靈活替代
2 交叉編譯
2.1 使用交叉編譯工具或者使用Golang指定目標編譯
2.2 windows
2.2.1 apt-get install gcc-mingw-w64 env CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc go build -o ./app/app.exe ./app/main.go env CGO_ENABLED=1 GOOS=windows GOARCH=386 CC=i686-w64-mingw32-gcc go build -o ./app/app.exe ./app/main.go
3 錯誤處理
3.1 官方推薦
3.1.1 error也是值,並且需要總是檢查error並對其處理
3.1.2 慎用或者不使用panic等手段
3.2 其他推薦
3.2.1 使用第三方errors庫
3.2.2 模仿Web開發模式,子模塊只管拋出異常,並在全局位置捕獲異常統一處理,即使用panic、recover、defer等聯合進行處理
3.2.3 defer參數值是在聲明時確定,而不是在調用時才確定
4 數據類型
4.1 變量與常量
4.1.1 全局變量使用var聲明,內部變量盡量使用簡短方式聲明/定義,需要明確指定變量類型的除外
4.1.2 常量可以指定為數值、布爾值或字符串等類型,格式:const constantName = value
4.1.3 同時聲明多個常量、變量,或者導入多個包時,可采用分組的方式進行聲明,適用於import/const/var
4.2 默認類型與默認值
4.2.1 整型、浮點型、復數默認類型分別為int/float64/complex128
4.2.2 默認值
4.2.2.1 bool-false, string-"", 整型、浮點型、復數-0
4.2.2.2 枚舉使用iota進行0值重置
4.2.2.3 array需要指定長度,討論其默認值沒有意義
4.2.2.4 初始聲明未初始化的slice/map默認為nil
4.2.3 初始化
4.2.3.1 聲明初始化、make/new初始化、賦值初始化
4.3 值類型
4.3.1 基本類型
4.3.1.1 rune-int32, int8, int16, int32-int, int64和byte-uint8, uint8, uint16, uint32, uint64
4.3.1.2 uintptr, intptr指針類型,用於指針操作
4.3.1.3 rune是int32的別稱,byte是uint8的別稱
4.3.2 字符串
4.3.2.1 字符串連接使用+運算符或者使用fmt格式化返回字符串
4.3.2.2 字符串為不可變類型,如果需要改變字符串值,需要轉化為[]byte/[]rune切片進行操作
4.3.2.3 字符串使用UTF-8編碼存儲,如果需要按照Unicode方式進行操作,需要轉化為[]rune切片或者使用"unicode/utf8"包
4.3.3 數組array
4.3.3.1 定義數組必須指定長度,且數組長度定義之后不能改變
4.3.3.2 數組賦值為值的賦值,參數傳遞也是值傳遞,即實參數組賦值一份數據給形參數組
4.3.4 結構體struct
4.3.4.1 struct與array一樣是值類型,作為參數會copy完整數據;slice、map、channel等傳遞的是數據的內存地址
4.3.4.2 struct不能使用make初始化,可以使用聲明方式或者new返回指針的方式初始化
4.3.4.3 通過非匿名字段實現對象的包含與持有,通過匿名字段實現傳統的類的繼承關系
4.3.4.4 與面向對象語言不同的是子類struct不能賦值給父類struct,即struct可以模擬面向對象,但不是為了實現完全的面向對象
4.3.4.5 字段繼承通過匿名字段實現;方法繼承/重寫也通過匿名字段實現
4.3.4.6 與傳統面向對象不同的是,golang中子類對象不能賦值給基類對象
4.4 引用類型
4.4.1 map、channel、slice為引用類型 如果兩個引用類型同時指向一個底層,那么一個改變,另一個也相應的改變 三者都可以通過for...range遍歷
4.4.2 切片slice
4.4.2.1 定義slice不指定長度,使用make可以指定長度
4.4.2.2 慎用append函數改變slice的長度,可能會異常改變被引用的數組內容,這可能是我們並不期望的結果
4.4.2.3 slice底層使用struct實現,slice的cap大小由golang動態實現
4.4.2.4 slice初始化,要么引用其他數組/切片,要么使用make/new初始化,make初始化可以指定初始大小
4.4.3 哈希map
4.4.3.1 定義map不需要指定長度,通過make指定長度也沒有意義,map本來就是動態的,即使指定長度沒有數據就認為len為0
4.4.3.2 map和其他基本型別不同,它不是thread-safe,在多個go-routine存取時,必須使用mutex lock機制
4.4.3.3 GET MAP兩個返回值, v, ok := mapa["C"], 如果不存在則ok為false
4.4.4 make與new
4.4.4.1 make用於內建類型(map、slice 和channel)的內存分配。new用於各種類型的內存分配
4.4.4.2 new返回指針, 且值為 nil; new(T)分配了零值填充的T類型的內存空間,並且返回其地址
4.4.4.3 make返回對象, 且值不為nil; make返回初始化后的(非零)值
4.4.5 channel
4.4.5.1 golang並發本質
4.4.5.1.1 PMG的原理,P對應操作系統進程--對程序的抽象,W對應操作系統線程--對寄存器的抽象, G對應goroutine--go實現的輕量級線程,也即GreenThreads用戶態線程。 一句話,golang提供了用戶態線程的管理機制。
4.4.5.1.2 G通過阻塞方式調用,M不會阻塞,G通過netpoller喚醒。一句話,用戶態線程通過netpoller方式避免阻塞系統線程。 當然,其他可能阻塞的地方golang的runtime也會做相應的優化,比如for循環也會被runtime優化。
4.4.5.1.3 goroutine+channel的本質是對生產車間流水線的完美模擬,應用開發者只要想清楚流水線該如何設計之后就能輕易理解 goroutine/channel的精妙,gorotine相當於很多干活的人,chennel相當於很多人之前的連線,點和線之間就可以設計復雜的工作流 理解了這一點就可以將問題抽象為,這個活兒能不能多個人一起干(多個goroutine), 這多個人怎么使用(chennel)有效的組織起來形成流水線?
4.4.5.2 無緩沖的channel可用於在多個goroutine之間同步,避免了顯示的加鎖。需要多個goroutine存在才能跑起來,只有一個goroutine會造成死鎖
4.4.5.3 有緩沖的channel是對無緩沖channel的補充,可以作為流水線緩沖隊列使用,一定程度上可以減輕無緩沖channel即阻塞式channel的壓力,提高流水線的整體性能
4.4.5.4 等待所有routine完成 需要考慮解決兩個問題
4.4.5.4.1 使用channel同步等待、使用sync.WaitGroup等待、主線程開goroutine等待並發出done信號,單個worker作為主goroutine
4.4.5.4.2 需要安全關閉channel
4.4.5.4.2.1 單worker輸出的channel可以由生產者主動關閉
4.4.5.4.2.2 多worker輸出的channel可以由第三方主動關閉,但要確認不會出現PANIC
4.4.5.4.2.3 下游goroutine使用for-range等待channel關閉
4.4.5.4.2.4 worker中select監控多個channel數據流動,接收done信號
4.4.5.4.2.5 最佳實踐參考
4.4.5.4.2.5.1 可以在worker函數內部自定義out channel
4.4.5.4.2.5.2 worker函數需要保證goroutine結束之后關閉out channel
4.4.5.4.2.5.3 worker函數如果有多個in channel,可以使用WaitGroup,等待所有in處理完畢之后關閉out
4.4.5.4.2.5.4 使用done channel close信號通知所有上游goroutine
4.4.5.4.2.5.5 最終消費者/主goroutine總是檢查out是否被關閉
4.4.5.4.2.6 總結
4.4.5.4.2.6.1 當所有發送操作結束時,每個階段都關閉自己的 outbound channels
4.4.5.4.2.6.2 每個階段都會一直從 inbound channels 接收數據,直到這些 channels 被關閉,或發送者解除阻塞狀態
4.5 其他
4.5.1 function
4.5.1.1 支持多返回值與變參,總是命名返回值,可以直接return
4.5.1.2 值類型(array/struct)為傳值調用--參數賦值,引用類型(channel/map/slice,指針等)為傳遞地址調用--參數引用
4.5.1.3 值傳遞&引用傳遞
4.5.1.3.1 傳值與傳址本質都是copy操作,一個copy數據,一個copy地址, copy數據不能改變實參,copy地址通過指針操作可以一定程度上改變實參的值, 但肯定不是隨意改變,比如append操作就不會改變原始實參
4.5.1.3.2 Go語言中channel,slice,map這三種類型的實現機制類似指針,所以可以直接傳遞,而不用取地址后傳遞指針 若函數需改變slice的長度(比如進行append操作),則仍需要取地址傳遞指針
4.5.1.3.3 函數參數中存在此概念,帶有接受者的函數也區分值傳遞與引用傳遞,也存在此概念
4.5.1.3.4 閉包通過非參數傳遞的方式直接引用外層代碼定義的變量,傳遞的是變量的地址
4.5.1.4 函數本身也可以作為類型,或者作為值傳遞給其他函數的形參,比如多種類型過濾器filter的實現即可參考這種模式
4.5.1.5 帶有接收者的函數即可實現面向對象--方法的繼承/重寫 雖然method的名字一模一樣,但是如果接收者不一樣,那么method就不一樣
4.5.1.6 struct、type自定義類型等都可以作為接受者
4.5.2 interface
4.5.2.1 接口A定義-B對象實現-C對象繼承B對象屬性-C也就繼承了B對象的接口/可以重寫-->類的繼承、方法的繼承、多態的實現
4.5.2.2 子類對象不能賦值給父類對象,但是接口可接受任意實現了該接口的對象,請注意這兩點與傳統面向對象設計語言的區別
4.5.2.3 任何對象都可以賦值給空interface,即可以存儲任意類型對象,類似於C語言的 C語言的void*
4.5.2.4 像Java一樣,接口也是可以通過extends繼承的
4.5.2.5 反射
4.5.2.5.1 golang的反射與Java相比功能弱很多,golang不支持解析string然后執行
4.5.2.5.2 golang的反射機制只能存在於已經存在的對象/類型上面
4.5.2.5.3 如果要實現與Java一樣的反射(RPC/Web框架)機制, 需要先把字符串和類的reflect.Typeof關聯好,然后根據字符串找到對應的類型,用reflect.New構造對象就可以了 即需要提前有注冊機制進行字符串<-->類型的關聯映射
5 流程控制
5.1 if 代碼塊內允許聲明臨時變量在if-else代碼塊內使用,使用分號;分割多條語句
5.2 for 既可以用來循環讀取數據,又可以當作while來控制邏輯,還能迭代操作,Golang中沒有while操作符 在嵌套For循環中,將循環次數多的循環放在內側,循環次數少的循環放在外側,其性能會提高 減少循環變量的實例化,其性能也會提高
5.3 switch 默認在case之后帶 break,如果需要繼續遍歷,可以使用fallthrough強制執行后面的case代碼
5.4 break, continue, return, goto 與 C 語言實現一樣
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM