詳解如何在golang項目開發(fā)中創(chuàng)建自己的Module
為什么要創(chuàng)建一個 Module?
我們?nèi)粘i_發(fā)程序的時候都會引入第三方的 package,使用第三方的 package 的好處是我們可以快速的開發(fā)我們的程序的功能,只需要專注我們自己項目的功能,而且第三方的 package 提供了強大的,豐富的功能模塊。比如 web 開發(fā)框架 gin, echo,微服務(wù)開發(fā)框架 go-micro、go-zero;權(quán)限控制:casbin,jwt: jwt-go, gorm:gorm, 存儲:minio , 編寫命令:cobra, 配置文件解析:viper,數(shù)據(jù)庫:mysql 等等。這些包都是以 module 的形式提供的服務(wù)。
在自己的團隊內(nèi)部同樣可以構(gòu)建自己的私有化的 module,工公司內(nèi)部的其他團隊使用。比如我們公司有三個項目組,都有一些共同的項目。所以我們有公共的 module 放到 gitlab 上面提供不同團隊的公共使用。同樣這樣也遵循了軟件設(shè)計的高內(nèi)聚低耦合的設(shè)計總則。將獨立的功能封轉(zhuǎn)為 module。
這樣做的好處是我們可以只實現(xiàn)一次共享使用,并不需要每個團隊中都各自實現(xiàn),同樣也會帶啦有好多弊端。
如何實現(xiàn)我們自己的 Module?
既然我們使用了很多開源的 module為我們的日常開發(fā)提供了很多的便捷性,那我們該如何實現(xiàn)自己的 module 來提供給團隊中使用,甚至可以開放到 gitlab 上面提供給所有人使用(取之于開源,饋與開源)。
通過一下步驟來實現(xiàn)一個其他人可以使用的 module
- 創(chuàng)建模塊:編寫一個小模塊,其中包含可以從另一個模塊調(diào)用的函數(shù)。
- 從另一個模塊調(diào)用您的代碼:導(dǎo)入并使用您的新模塊。
- 返回并處理錯誤: 添加簡單的錯誤處理。
- 處理切片中的數(shù)據(jù)(Go 的動態(tài)大小數(shù)組)。
- 添加測試程序:使用 Go 的內(nèi)置單元測試功能來測試您的代碼。
創(chuàng)建一個 module 目錄
首先創(chuàng)建一個 Go 模塊。在模塊中,我們可以將一組有用的功能在一個或多個相關(guān)包中實現(xiàn)。
Go 代碼被分組為 package,package 又被分組為module。module 中需要指定運行代碼所需的依賴項,包括 Go 版本及其所需的其他模塊。
1、打開控制臺并cd
轉(zhuǎn)到工作目錄中,并創(chuàng)建目錄module/greetings
:
?mkdir module ?cd module/greetings/
2、go mod init
命令初始化模塊 。
運行go mod init
命令,為其提供模塊路徑 - 此處使用example.com/greetings
. 如果發(fā)布模塊,則這必須是 Go 工具可以下載您的模塊的路徑。那將是我們的代碼的存儲庫。
?$ go mod init example.com/greetings
該go mod init
命令創(chuàng)建一個 go.mod 文件來跟蹤代碼的依賴項。當(dāng)我們添加依賴項時,go.mod 文件將列出項目的代碼所依賴的版本。這可以保持構(gòu)建的可重復(fù)性,并讓我們可以直接控制要使用的模塊版本。
3、打開編輯器在greetings
創(chuàng)建目錄下創(chuàng)建 文件greetings.go
并編寫代碼:
?package greetings ?? ?import ( ? "fmt" ? "math/rand" ?) ?func Hello(name string) string { ? return fmt.Sprintf("嗨,%v。歡迎!", name) ?}
該函數(shù)Hello 接受一個name
類型為的參數(shù) string
。該函數(shù)還返回一個string
。在Go中,名稱以大寫字母開頭的函數(shù)可以被不在同一包中的函數(shù)調(diào)用。這在 Go 中稱為導(dǎo)出名稱。
從另一個模塊調(diào)用您的代碼
1、我們在 greeting 的同級目錄創(chuàng)建 hello 目錄,作為調(diào)用者。
?~/Developer/module ?tree -d ?. ?├── greetings ?└── hello
2、打開編輯器并創(chuàng)建文件 hello.go 文件。在 hello.go 文件中編寫代碼:
?import ( ? "example.com/greetings" ? "log" ?) ?? ?func main() { ? message := greetings.Hello("demo007") ? ?fmt.Println(message) ?}
在文件中聲明一個main
包。在 Go 中,作為應(yīng)用程序執(zhí)行的代碼必須位于包中main
。
導(dǎo)入兩個包:example.com/greetings
和fmt
。 導(dǎo)入 example.com/greetings
可以訪問該Hello
函數(shù)。還可以導(dǎo)入fmt
, 以及處理輸入和輸出文本的函數(shù)(例如將文本打印到控制臺)。greetings
通過調(diào)用包的 函數(shù) 來獲取輸出信息。
3、編輯example.com/hello
模塊以使用本地 example.com/greetings
模塊。
example.com/greetings
對于生產(chǎn)使用, 可以從其存儲庫發(fā)布模塊,Go 工具可以在其中找到它并下載它。但是由于我們尚未發(fā)布該模塊,因此我們需要調(diào)整該模塊,以便它可以在本地文件系統(tǒng)上 example.com/hello
找到代碼example.com/greetings
。
所以我們使用go mod edit
命令編輯example.com/hello
模塊,將 Go 工具從其模塊路徑(模塊所在的位置)重定向到本地目錄(模塊所在的位置)。
在 hello 目錄中的命令提示符下,運行以下命令:
?$ go mod edit -replace example.com/greetings=../greetings
該命令指定example.com/greetings
應(yīng)替換為../greetings
以便查找依賴項。運行命令后,hello 目錄中的 go.mod 文件應(yīng)包含一條replace的指令。
?module example.com/hello ?? ?go 1.20 ?? ?replace example.com/greetings => ../greetings ?? ?require example.com/greetings v0.0.0-00010101000000-000000000000 ??
然后我們在命令中 cd hello 的目錄中,并執(zhí)行命令 go mod tidy
, 執(zhí)行完成后我們發(fā)現(xiàn)新增加一行 require example.com/greetings v0.0.0-00010101000000-000000000000
并制定了一個版本號.
我們在 hello 中執(zhí)行運行命令查看:
?go run hello.go ?嗨,demo007。歡迎!
返回并處理錯誤
處理錯誤是可靠代碼的一個基本特征。下面我們將添加一些代碼以從 greetings 模塊返回錯誤,然后在調(diào)用者中處理它。
在 greetings/greetings.go
中,修改代碼:
?package greetings ?? ?import ( ? "errors" ? "fmt" ? "math/rand" ?) ?? ?func Hello(name string) (string, error) { ? // 如果沒有提供名稱,則返回帶有錯誤消息的錯誤。 ? if name == "" { ? return name, errors.New("empty name") ? } ?? ? // 使用隨機格式創(chuàng)建一條消息。 ? message := fmt.Sprintf("嗨,%v。歡迎!", name) ? return message, nil ?}
我們修改了 Hello 函數(shù)添加了返回值 error
,if
語句來檢測輸入的 name 是否是一個有效的值。如果為空則返回name 和 一個 錯誤 errors.New("empty name")
在 hello/hello.go
文件中,處理函數(shù)現(xiàn)在返回的錯誤 Hello
以及非錯誤值。
?package main ?? ?import ( ? "example.com/greetings" ? "fmt" ? "log" ?) ?? ?func main() { ? message, err := greetings.Hello("demo007") ? if err != nil { ? log.Fatal(err) ? } ? fmt.Println(message)
使用標(biāo)準(zhǔn)庫中的函數(shù)log package
輸出錯誤信息。如果出現(xiàn)錯誤,可以使用 log
包的Fatal
來打印錯誤并停止程序。
在目錄中的命令行中hello
,運行 hello.go 以確認(rèn)代碼有效
?go run hello.go ?Great to see you, demo007!
處理切片中的數(shù)據(jù)
我們將使用 Go 切片。切片類似于數(shù)組,只不過它的大小會隨著添加和刪除項目而動態(tài)變化。切片是 Go 最有用的類型之一。
我們將添加一個小切片來包含三條問候消息,然后讓代碼隨機返回其中一條消息。
在 greetings/greetings.go 中,我們修改代碼,如下所示。
?package greetings ?? ?import ( ? "errors" ? "fmt" ? "math/rand" ?) ?? ?func Hello(name string) (string, error) { ? // 如果沒有提供名稱,則返回帶有錯誤消息的錯誤。 ? if name == "" { ? return name, errors.New("empty name") ? } ?? ? // 使用隨機格式創(chuàng)建一條消息。 ? message := fmt.Sprintf(randomFormat(), name) ? return message, nil ?} ?? ?func randomFormat() string { ? formats := []string{ ? "Hi, %v. Welcome!", ? "Great to see you, %v!", ? "Hail, %v! Well met!", ? } ? return formats[rand.Intn(len(formats))] ?}
1、添加一個randomFormat
函數(shù),返回隨機選擇的問候消息格式。 randomFormat
以小寫字母開頭,使其只能被其自己的包中的代碼訪問(換句話說,它不會被導(dǎo)出)。
2、在 中randomFormat
,聲明formats
具有三種消息格式的切片。聲明切片時,您可以在括號中省略其大小,如下所示:[]string
。這告訴 Go 切片底層數(shù)組的大小可以動態(tài)更改。
3、使用該 math/rand
包生成一個隨機數(shù),用于從切片中選擇一個項目。
4、在 中Hello
,調(diào)用該randomFormat
函數(shù)來獲取您將返回的消息的格式,然后 name
一起使用該格式和值來創(chuàng)建消息。
5、像以前一樣返回消息以及錯誤
在 hello/hello.go 中,我們修改代碼,如下所示:
?package main ?? ?import ( ? "example.com/greetings" ? "fmt" ? "log" ?) ?? ?func main() { ? message, err := greetings.Hello("demo007x") ? // 如果返回了錯誤,請將其打印到控制臺并退出程序。 ? if err != nil { ? log.Fatal(err) ? } ? //如果沒有返回錯誤,請將返回的消息打印到控制臺。 ? fmt.Println(message) ?} ??
我們運行代碼看看輸出的內(nèi)容:
go run hello.go
Hail, demo007x! Well met!
?
go run hello.go
Great to see you, demo007x!
?
go run hello.go
Hi, demo007x. Welcome!
測試 module
以上我們編寫了基本的或者說是最簡單的 golang module 的程序項目,如何確保我們的程序是無錯誤的或者是沒有 bug 的?最好的辦法就是添加 測試程序。
在開發(fā)過程中測試代碼可能會暴露出在您進行更改時出現(xiàn)的錯誤。
Go 對單元測試的內(nèi)置支持使我們可以更輕松地進行測試。具體來說,使用命名約定、Go 的testing
包和go test
命令,可以快速編寫和執(zhí)行測試。
在greetings目錄中,創(chuàng)建一個名為greetings_test.go的文件。
以 _test.go 結(jié)尾的文件名告訴
go test
命令該文件包含測試函數(shù)。在greetings_test.go中,編寫以下代碼并保存文件:
?package greetings ?? ?import ( ? "regexp" ? "testing" ?) ?? ?// TestHelloName 調(diào)用 greetings.Hello 并傳入一個名稱,檢查返回值是否有效。 ?func TestHelloName(t *testing.T) { ? name := "Gladys" ? want := regexp.MustCompile(`\b` + name + `\b`) ? msg, err := Hello("Gladys") ? if !want.MatchString(msg) || err != nil { ? t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want) ? } ?} ?? ?// TestHelloEmpty 調(diào)用 greetings.Hello 并傳入空字符串, 檢查是否出現(xiàn)錯誤。 ?func TestHelloEmpty(t *testing.T) { ? msg, err := Hello("") ? if msg != "" || err == nil { ? t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err) ? } ?}
創(chuàng)建兩個測試函數(shù)來測試該
greetings.Hello
函數(shù)。測試函數(shù)名稱的形式為,其中Name表示有關(guān)特定測試的信息。此外,測試函數(shù)將指向包的指針作為參數(shù)??梢允褂么藚?shù)的方法來報告和記錄測試。兩個測試:
TestHelloName
調(diào)用該Hello
函數(shù),傳遞一個name
值,該函數(shù)應(yīng)該能夠返回有效的響應(yīng)消息。如果調(diào)用返回錯誤或意外響應(yīng)消息(不包含您傳入的名稱),則可以使用參數(shù)t
的方法將消息打印到控制臺并結(jié)束執(zhí)行Fatalf。TestHelloEmpty
Hello使用空字符串調(diào)用該函數(shù)。此測試目的在確認(rèn)錯誤處理是否有效。如果調(diào)用返回非空字符串或沒有錯誤,則可以使用t
參數(shù)的Fatalf
方法將消息打印到控制臺并結(jié)束執(zhí)行。
在greetings目錄下的命令行中,運行命令 go test 執(zhí)行測試。
?go test -v ?=== RUN ? TestHelloName ?--- PASS: TestHelloName (0.00s) ?=== RUN ? TestHelloEmpty ?--- PASS: TestHelloEmpty (0.00s) ?PASS ?ok example.com/greetings 0.007s
通過測試輸出的結(jié)果可以看到我們的程序全部都通過了測試。
發(fā)布module
以上我們編寫了我們的第一個 go module, 并且編寫了對應(yīng)函數(shù)的測試程序,然后我們可以將我們的 module 程序提交到 github等系統(tǒng)。其他人就可以通過執(zhí)行命令 go get example.com/greetings
將 greeting 包安裝到自己的項目中。
總結(jié):
golang module 給我們提供了靈活的本地開發(fā)并測試 module 的機制。我們通過修改 go.mod
文件,通過指令 replace example.com/greetings => ../greetings
來實現(xiàn)本地 module 的開發(fā)和測試的全部過程。
以上就是詳解golang項目開發(fā)如何創(chuàng)建自己的Module的詳細內(nèi)容,更多關(guān)于golang創(chuàng)建Module的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go語言日志實現(xiàn)詳解(打印日志、日志寫入文件和日志切割)
golang內(nèi)置了log包,實現(xiàn)簡單的日志服務(wù),下面這篇文章主要給大家介紹了關(guān)于go語言日志實現(xiàn)(打印日志、日志寫入文件和日志切割)的相關(guān)資料,需要的朋友可以參考下2022-10-10Go 并發(fā)編程Goroutine的實現(xiàn)示例
Go語言中的并發(fā)編程主要通過Goroutine和Channel來實現(xiàn),本文就來介紹一下Go 并發(fā)編程的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12關(guān)于go-zero單體服務(wù)使用泛型簡化注冊Handler路由的問題
這篇文章主要介紹了go-zero單體服務(wù)使用泛型簡化注冊Handler路由,涉及到Golang環(huán)境安裝及配置Go Module的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07golang基礎(chǔ)之字符串與int、int64類型互相轉(zhuǎn)換
這篇文章主要給大家介紹了關(guān)于golang基礎(chǔ)之字符串與int、int64類型互相轉(zhuǎn)換的相關(guān)資料,在Go語言中string轉(zhuǎn)int是一項常見的操作,需要的朋友可以參考下2023-07-07