golang進階介紹part-3(佈局的概念)
前言
之前介紹了一些Golang的基礎使用,從學習的角度上demo化了比較重要的幾個要點,這一篇主要介紹一些從個人角度以及開發習慣上對於Golang佈局的理解。
1. (golang package)vs(.net nuget/ java jar )
我本人之前是一個csharper,第一門接觸的語言是php,所以在學golang的時候會不自覺的是用傳統概念的三層架構去理解golang的佈局,有的時候會發現,怎麼寫程式碼是如此的變扭,越寫越懷疑golang這語言寫起來怎麼那麼尷尬,為此我特地問了些先行者,也網上看了一些golang如何佈局的帖子,最後我總結了原因,後來別人問我我都是那麼回答的,可能有不足之處。
golang的package的導包比較特殊,當import了一個package後,該package的東西就可以使用了,這在我看來有點類似於領域驅動的概念,又有點元件化的概念,也和golang的特性直接相關,因為Golang的interface程式設計更貼合組裝式的。
java或者.net則是繼承式的,當然也有interface的概念,但是因為有天然的繼承,所以interface也會向繼承的概念側重,並且目前最流行的IOC的程式設計,更加讓這些語言向繼承傾斜,所以他們的包管理適合全量,簡單的專案分類可以側重model, business,data式的分層。
這恰恰是該類開發者轉golang的缺點所在,因為對於這樣的程式設計模式太熟悉了,導致概念的轉換顯得有些笨拙。
golang的import式程式設計目的在於讓專案按功能塊劃分的更細,一個小業務塊有一個自己的包,多包之間可以是並列的,更像最近火起的微服務概念的抽想,我們可以通過golang開源專案docker(現在叫moby)的開源庫來理解,其中container和image各有一個屬於自己的包,如果以java或者.net來,很可能這2個都是model層的一個class了。
在我自己的編寫的專案中,我是這樣理解的,拿區塊鏈錢包舉例,幣種的概念和交易以及公私鑰和後臺爬塊這4個點我會抽象成 coin、trans、key、daemon,這樣開源後,如果別人感覺我的coin包寫的好,那麼就直接借用吧,並不需要其他3個包,如果他覺得key的公私鑰管理包很適合他,其他的沒有可取之處,那麼走您,import key包。
golang更考驗開發者對於物件化概念的抽象,如何量化一個package的邊界,如何實現package之間的依賴,這些都是學問。
2. interface 與 struct 結合使用
- 相對於面向物件的封裝、繼承、多型,還是以區塊鏈為例,多種主鏈幣之間,每個幣種也業務實現都有一定自由規則,但是概念上區塊鏈交易概念是類似的,我們可以介面化約束幣種錢包的功能,使得更易於業務邏輯的管理
type BTC struct{ } type ETH struct{ } type Walleter interface { Transfer() GetBalance(account string)int } func(*BTC)Transfer(){ } func(*BTC)GetBalance(account string)int{ } func(*ETH)Transfer(){ } func(*ETH)GetBalance(account string)int{ }
因為上面的封裝,我們可以使主流程單一化,另外一點interface把一個業務塊的邏輯體現在了幾行簡短的程式碼塊中,也是對業務梳理的一個提綱,一個目錄,所以即使不是多型的概念,也可以適當的運用interface。
- 然而有時候我們還會需要資料庫的操作
type UserStorage struct{ Save(account string,balance int) Remove() } type BlockChain struct{ db*UserStorage Walleter } func(bc *BlockChain)GetBalance(account string){ bal := bc.Walleter.GetBalance(account) bc.db.Save(account,bal) }
如此封裝的好處在於db的引用全是在BlockChain內部的,方便儲存介質的替換,相比於db邏輯傳參進來更易於維護和拓展,如果有BlockChain,BlockChain1,BlockChain2都這樣使用,那麼我們替換BlockChain1時,不管是查詢引用,還是模組化替換都更加工程化。
- 另一外一個interface的妙用就是,將struct解除對外的暴露,直接暴露interface,所有需要對外提供的資料都已interface的func對外返回,如果開發者覺暴露struct會導致其他開發者呼叫中引起問題的話。
type core struct{ } type Corer interface{ GetCoreNum() SetCoreName() } func NewCore()Corer{ }
3.go和channel的使用
這2個是Golang的一個關鍵點所在,具體原理我在golang 基礎介紹part-2中介紹過,然而對於go和channel的使用,我個人認為,一定要在把控好的基礎上使用,比如go併發量一旦大過頭了可能會讓資料庫讀寫跟不上,channel亂用會導致一個流程不見底,這些都會導致程式碼的後期拓展和維護存在大問題,一個專案的好壞是把複雜的邏輯寫的更簡單易懂,大道若簡,而不是為了炫技把一個簡單的邏輯複雜化導致維護拓展成本的上升
4. 單元測試,效能測試
關於單元測試,很多程式員都不喜歡自己寫,當初我在開發.NET專案時,起初也是認為我的程式碼沒問題,我能通過自己的方法確保他是OK的,但是在開發過程中,其實變相的還是寫了很多單元測試,因為要確保邏輯通啊,方便修改啊,方便除錯啊,在golang專案中,test的編寫更加容易,單元測試的編寫能夠讓你設計的專案結構依賴更順暢,如果單元測試不好寫,那麼就說明專案結構有問題了,需要反思一下了,如何才能讓單元測試的編寫更簡單,這也是一個反哺的過程,編寫單元測試越容易說明程式碼結構解耦的越好,越健壯。
效能測試也是不得不提的一個點,在程式碼優化時,他能計算出的在並行狀態下的運算時間,消耗了多少記憶體,例項化了多少個物件等等,能讓人更好的把控自己的程式碼,同時也加深了對於Golang原理的瞭解,有助於編寫出更加優秀的程式碼。
對於單元測試的資料及啟動項,我個人習慣加一個Mock包用作全域性測試資料的管理,全域性的test直接應用這個包使用依賴或者資料,這樣整個專案的啟動依賴也體現在了mock包中,這樣測試和實際就牢牢結合在了一起。
5. 專案結構方面
ofollow,noindex">Golang 標準包佈局[譯]
這邊帖子很好。