偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Go 命令行工具項(xiàng)目結(jié)構(gòu)最佳實(shí)踐

開發(fā) 前端
本文針對(duì)Golang實(shí)現(xiàn)的命令行小工具項(xiàng)目結(jié)構(gòu)做了詳細(xì)介紹,同時(shí)介紹了其他主流Go項(xiàng)目結(jié)構(gòu)。

 最近我在重構(gòu)早期實(shí)現(xiàn)的命令行工具項(xiàng)目,在對(duì)項(xiàng)目結(jié)構(gòu)做改動(dòng)的過程中我沒看到有一個(gè)Go 語言項(xiàng)目結(jié)構(gòu)最佳實(shí)踐。

其他語言都有這樣一個(gè)比較推薦的項(xiàng)目結(jié)構(gòu),比如Java就有典型項(xiàng)目結(jié)構(gòu),開發(fā)者在這個(gè)項(xiàng)目結(jié)構(gòu)下進(jìn)行開發(fā)即可。Python使用Django和Flask時(shí),框架會(huì)直接定義好項(xiàng)目結(jié)構(gòu)。但是Go社區(qū)還沒有在項(xiàng)目結(jié)構(gòu)這件事上實(shí)際達(dá)成一致。

雖然已經(jīng)有一些比較推薦的項(xiàng)目結(jié)構(gòu)出現(xiàn)了,不過普遍推薦的結(jié)構(gòu)對(duì)于我的項(xiàng)目并不適用。本文將探討我的項(xiàng)目最終結(jié)構(gòu)并與標(biāo)準(zhǔn)最佳實(shí)踐做對(duì)比。

用良好的package設(shè)計(jì)構(gòu)建項(xiàng)目

第一個(gè)最佳實(shí)踐是,項(xiàng)目中任何可重用的代碼都要做成一個(gè)package。如何設(shè)計(jì)package結(jié)構(gòu)和有關(guān)package的最佳時(shí)間就需要單獨(dú)寫篇文章,我做過一次關(guān)于這個(gè)內(nèi)容的分享,ppt連接在下面:

https://go-pkg-structure.dev/

把代碼放進(jìn)一個(gè)個(gè)package要比僅僅重用代碼的好處大得多。從項(xiàng)目結(jié)構(gòu)的角度來說,把代碼放進(jìn)獨(dú)立package有助于把一個(gè)個(gè)擁有獨(dú)立功能的代碼進(jìn)行分組,這樣更方便其他參與開發(fā)的開發(fā)者維護(hù)代碼,這對(duì)開源項(xiàng)目來說意義重大。

獨(dú)立成一個(gè)個(gè)package的做法能讓項(xiàng)目測(cè)試起來更容易。把功能獨(dú)立成一個(gè)個(gè)package,就能用更少的依賴對(duì)一個(gè)個(gè)功能進(jìn)行測(cè)試。

在創(chuàng)建和重構(gòu)項(xiàng)目時(shí),我第一件事就是寫好項(xiàng)目需要的package,甚至?xí)趯懘a之前創(chuàng)建好基本項(xiàng)目結(jié)構(gòu)。

把應(yīng)用程序邏輯和接入層邏輯分開

另一個(gè)我看到用的比較多的最佳實(shí)踐是把應(yīng)用代碼和接入層代碼分離開,這里接入代碼指的是main包和main()方法。

Go和其他語言一樣,應(yīng)用的接入層代碼是main方法,當(dāng)應(yīng)用開始運(yùn)行時(shí)就是最先執(zhí)行的一部分邏輯,很可能就把所有初始化邏輯都只寫在main方法里了。把各自初始化邏輯放在app包內(nèi)實(shí)現(xiàn)是比全寫在main方法里更好的做法。

把初始化邏輯放到各自package下是更好的做法,這樣也更方便做測(cè)試。比如把Start() Stop() Shutdown()方法都放到app包內(nèi),寫測(cè)試代碼時(shí)就可以在當(dāng)前包中調(diào)用啟動(dòng)停止這些功能了。

下面是一個(gè)app包內(nèi)的實(shí)現(xiàn)例子:

  1. package app 
  2.  
  3. import ( 
  4.  
  5. "fmt" 
  6.  
  7.  
  8. var ErrShutdown = fmt.Errorf("application was shutdown gracefully"
  9.  
  10. func Start(...) error { 
  11.  
  12. // Application runtime code goes here 
  13.  
  14.  
  15. func Shutdown() { 
  16.  
  17. // Shutdown contexts, listeners, and such 
  18.  

如果你的命令行工具項(xiàng)目里,既有服務(wù)端代碼也有客戶端代碼,在一個(gè)app文件夾內(nèi)實(shí)現(xiàn)的邏輯就能被服務(wù)端和客戶端共享。

然而這個(gè)做法對(duì)簡(jiǎn)單的命令行應(yīng)用不友好,這些應(yīng)用可能是啟動(dòng)-執(zhí)行-停止的模式。但我依然選擇使用把邏輯放到app目錄下的做法,這樣可以把運(yùn)行時(shí)邏輯都放在一起,降低了其他開發(fā)者對(duì)這個(gè)項(xiàng)目的理解難度。

main package里該放些什么?

把我們所有應(yīng)用都放到app包里之后,也要考慮main包里有什么。很簡(jiǎn)單,main包里只有很少內(nèi)容。

總的來說,我會(huì)把main包限制為“只放與用戶交互的代碼”。例如,如果我的項(xiàng)目里既有cli又有服務(wù)端邏輯, 我通常會(huì)將命令行參數(shù)解析的邏輯放入main包中。服務(wù)端和客戶端cli編譯的二進(jìn)制文件會(huì)包含不同包,通過解析主程序包中的參數(shù),就可以為不同cli創(chuàng)建獨(dú)立的選項(xiàng)。

其他需要和用戶交互的命令行應(yīng)用,我也傾向于放進(jìn)main包,例如:

  • 命令行參數(shù)的解析
  • 用戶輸入(很簡(jiǎn)單的輸入、不參與核心邏輯)
  • 解析配置文件
  • 退出邏輯
  • 處理信號(hào)

下面的代碼是一個(gè)main方法例子:

  1. // main runs the command-line parsing and validations. This function will also start the application logic execution. 
  2.  
  3. func main() { 
  4.  
  5. // Parse command-line arguments 
  6.  
  7. var opts options 
  8.  
  9. args, err := flags.ParseArgs(&opts, os.Args[1:]) 
  10.  
  11. if err != nil { 
  12.  
  13. os.Exit(1
  14.  
  15.  
  16. // Convert to internal config 
  17.  
  18. cfg := config.New() 
  19.  
  20. cfg.Verbose = opts.Verbose 
  21.  
  22. // more taking command line options and putting them into a config struct. 
  23.  
  24. if opts.Pass { 
  25.  
  26. // ask the user for a password 
  27.  
  28.  
  29. // Run the App 
  30.  
  31. err = app.Run(cfg) 
  32.  
  33. if err != nil { 
  34.  
  35. // do stuff 
  36.  
  37. os.Exit(1
  38.  
  39.  

一種推薦的項(xiàng)目結(jié)構(gòu)

有種被推薦了很多的一種項(xiàng)目結(jié)構(gòu)如下:

  • internal/app - 僅在內(nèi)部使用的核心應(yīng)用功能
  • internal/pkg/ - 僅在內(nèi)部使用的package
  • pkg/ - 需要和外部代碼進(jìn)行分享的package
  • cmd/<app_name> - 把main package放在帶有app名稱的這個(gè)目錄下

這個(gè)推薦結(jié)構(gòu)的一個(gè)重點(diǎn)在于把核心代碼放在internal/app、入口代碼放進(jìn)cmd/<app_name>。這種結(jié)構(gòu)對(duì)于一次編譯出好幾個(gè)二進(jìn)制文件的項(xiàng)目來說非常友好,比如一次編譯出server和cli的項(xiàng)目。cmd/<app_name>應(yīng)當(dāng)包含cli的main方法,cmd/<app_name>-server目錄下放服務(wù)端的main方法。這兩者可以在internal/app目錄下共享其他代碼。

總的來說這也是個(gè)不錯(cuò)的目錄結(jié)構(gòu),但是這個(gè)目錄結(jié)構(gòu)對(duì)我不適用,看看我是怎么改的吧。

我把package放到了其他路徑下

我做的與上一段的推薦結(jié)構(gòu)不同的是package的路徑。應(yīng)用程序項(xiàng)目結(jié)構(gòu)的子目錄太多,這與獨(dú)立項(xiàng)目結(jié)構(gòu)不同,我也不喜歡用應(yīng)用程序項(xiàng)目結(jié)構(gòu)組織代碼。我認(rèn)為,太多子目錄阻礙開發(fā)者找到功能實(shí)現(xiàn)的代碼。

子目錄多,對(duì)代碼量很大的重量級(jí)項(xiàng)目來可能比較有必要,但最好不要對(duì)小型中型項(xiàng)目使用這種項(xiàng)目結(jié)構(gòu)。

我選擇把所有package都放在代碼根目錄這一層,例如我有個(gè)Parser包,它的路徑就是parser/,ssh包的路徑就是ssh/,app包路徑是app/。

這個(gè)做法使找包和功能都很容易,因?yàn)榘痛a都在項(xiàng)目第一層。再次強(qiáng)調(diào)下,把所有包都放在目錄第一層的做法適用于包數(shù)量不大的項(xiàng)目,如果項(xiàng)目包數(shù)量變多,那還是把包放到pkg/路徑下靠譜。

我沒有采用internal和pkg模式

我并不覺得把代碼放進(jìn)internal/或者pkg/這種實(shí)踐好,主要原因在于這種實(shí)踐是針對(duì)app內(nèi)部包。但是關(guān)于app內(nèi)部包并沒有明確的“內(nèi)”“外”劃分。對(duì)于僅在內(nèi)部使用的包,很多開發(fā)者就會(huì)因?yàn)?quot;沒有其他人使用這些包"所以根本也沒有用最佳實(shí)踐。

我也不希望開發(fā)者在pkg路徑下像維護(hù)一個(gè)個(gè)獨(dú)立項(xiàng)目一樣維護(hù)代碼。實(shí)際開發(fā)中,這些包內(nèi)的接口可能和一個(gè)個(gè)獨(dú)立項(xiàng)目一樣做變動(dòng),那么如果這些邏輯真的是一個(gè)個(gè)分離開的,還不如放到獨(dú)立的項(xiàng)目里實(shí)現(xiàn)。

對(duì)我來說把我所有項(xiàng)目?jī)?nèi)部代碼都放到同一個(gè)文件夾下更合理。要么是放在頂層目錄下要么是放在pkg/目錄下。

我沒有把所有文件都放進(jìn)cmd/目錄

cmd/目錄不適用于我的項(xiàng)目。我這個(gè)項(xiàng)目里有一個(gè)簡(jiǎn)單的CLI應(yīng)用,這個(gè)應(yīng)用要方便使用者下載安裝。最快最方便的安裝辦法是使用go get命令安裝:

  1. $ go get -u github.com/madflojo/efs2 

我想要用戶只需要用go get加項(xiàng)目url就能安裝,但是如果用了cmd目錄就需要讓用戶在url基礎(chǔ)上增加/cmd/<app_name>才能安裝:

  1. $ go get -u github.com/madflojo/efs2/cmd/efs2 

這個(gè)url格式比較亂,用戶還需要知道我項(xiàng)目結(jié)構(gòu)是怎么樣的才能安裝。我希望項(xiàng)目結(jié)構(gòu)能讓別人更方便而不是更麻煩。所以我就把這個(gè)小應(yīng)用的main.go文件放到了項(xiàng)目的頂層文件夾下,這樣用戶就可以直接通過go get命令安裝應(yīng)用了,另把應(yīng)用的功能實(shí)現(xiàn)都放在app包內(nèi)。

總結(jié)

本文的模式應(yīng)用結(jié)果如下:

  1. $ tree -L 2 
  2.  
  3.  
  4. ├── CONTRIBUTING.md 
  5.  
  6. ├── Dockerfile 
  7.  
  8. ├── LICENSE 
  9.  
  10. ├── Makefile 
  11.  
  12. ├── README.md 
  13.  
  14. ├── app 
  15.  
  16. │ ├── app.go 
  17.  
  18. │ └── app_test.go 
  19.  
  20. ├── config 
  21.  
  22. │ ├── config.go 
  23.  
  24. │ └── config_test.go 
  25.  
  26. ├── dev-compose.yml 
  27.  
  28. ├── go.mod 
  29.  
  30. ├── go.sum 
  31.  
  32. ├── main.go 
  33.  
  34. ├── parser 
  35.  
  36. │ ├── parser.go 
  37.  
  38. │ └── parser_test.go 
  39.  
  40. ├── ssh 
  41.  
  42. │ ├── ssh.go 
  43.  
  44. │ └── ssh_test.go 
  45.  
  46. └── vendor 
  47.  
  48. ├── github.com 
  49.  
  50. ├── golang.org 
  51.  
  52. └── modules.txt 
  53.  
  54. 7 directories, 18 files 

總的來說我對(duì)這個(gè)結(jié)構(gòu)很滿意,新開發(fā)者也可以比較快地上手代碼。這個(gè)結(jié)構(gòu)基本看目錄就可以知道里面的功能了。

把代碼功能一點(diǎn)點(diǎn)拆分到不同package中也幫助我提升了代碼覆蓋率,目前main包放在頂層目錄的做法還沒發(fā)現(xiàn)任何弊端。當(dāng)然這種項(xiàng)目結(jié)構(gòu)可能并不會(huì)對(duì)所有人適用,項(xiàng)目開發(fā)中還是要因地制宜。

 

責(zé)任編輯:張燕妮 來源: Go開發(fā)大全
相關(guān)推薦

2014-02-12 13:30:16

Linux命令行終端工具

2014-07-31 10:09:12

Linux命令歸檔工具

2023-12-01 07:06:14

Go命令行性能

2023-10-30 01:00:42

Go語言Cobra庫(kù)

2020-12-10 16:16:08

工具代碼開發(fā)

2020-12-11 06:44:16

命令行工具開發(fā)

2023-06-09 07:45:29

Kuberneteskubectl

2023-03-31 08:44:55

Go開發(fā)命令

2020-12-08 08:46:07

GoJava工具

2013-12-09 14:29:13

OpenStack命令行工具API

2010-11-24 17:12:17

MySQL命令行

2011-01-18 19:11:26

Postfix命令行

2019-08-27 10:32:01

Linux操作系統(tǒng)Windows

2018-05-04 09:15:35

PythonPlumbum命令行

2013-11-15 09:43:15

JDK工具

2013-05-21 14:58:08

系統(tǒng)監(jiān)視glances開源

2021-11-28 22:16:01

Go語言工具

2014-08-25 16:23:24

2018-07-05 08:30:54

Python命令行工具shell

2018-04-03 13:50:27

Linux容器命令行工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)