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