學(xué)習(xí)筆記20170601">【PMP】學(xué)習(xí)筆記20170601
930
2025-03-31
前兩天一直關(guān)注的《Go語(yǔ)言實(shí)戰(zhàn)》終于拿到手了,這本書(shū)期待了很久,預(yù)售的時(shí)候就第一時(shí)間進(jìn)行了預(yù)定。昨天閑暇時(shí)間翻了前三章,覺(jué)得還不錯(cuò),所以打算針對(duì)該書(shū)籍,寫一個(gè)讀書(shū)筆記系列,這是對(duì)自己看書(shū)的一個(gè)總結(jié),也是和大家一個(gè)分享。
這本是In Action系列的書(shū)籍,這個(gè)系列做研發(fā)的都知道,在研發(fā)屆評(píng)價(jià)很多,很多新的技術(shù)、語(yǔ)言等都會(huì)有一本實(shí)戰(zhàn)的書(shū)籍。既然是實(shí)戰(zhàn),那么這本書(shū)假設(shè)了他的讀者有了一定的基礎(chǔ),比如這本書(shū)的讀者要有一定的Go語(yǔ)言基礎(chǔ),比如Go開(kāi)發(fā)環(huán)境搭建,Go的內(nèi)置類型、Go的常用關(guān)鍵字等等。
不過(guò)我公眾號(hào)[flysnow_org]里,既然打算出一個(gè)《Go語(yǔ)言實(shí)戰(zhàn)》的讀書(shū)筆記,就不需要大家有Go基礎(chǔ)了,書(shū)里沒(méi)講的知識(shí)點(diǎn),我也會(huì)在我的讀書(shū)筆記里介紹,當(dāng)然還有深入。
什么是Go語(yǔ)言中的包
這里我們直接講Go語(yǔ)言實(shí)戰(zhàn)的筆記,就不介紹Go語(yǔ)言的入門了,要入門,可以參考我的這篇文章 Go語(yǔ)言開(kāi)發(fā)環(huán)境搭建詳解
我們?cè)谑褂闷渌Z(yǔ)言,比如Java,是有包的概念的,它是Java語(yǔ)言中組織我們的Java文件的一個(gè)概念,比如java.lang這個(gè)包,他里面有很多我們常用的類,比如String。在Go語(yǔ)言中,包也是類似的概念,它是把我們的go文件組織起來(lái),可以方便進(jìn)行歸類、復(fù)用等目的。 比如Go內(nèi)置的net包
net ├──?http ├──?internal ├──?mail ├──?rpc ├──?smtp ├──?testdata ├──?textproto └──?url
以上是net包的一個(gè)目錄結(jié)構(gòu),net本身是一個(gè)包,net目錄下的http又是一個(gè)包。從這個(gè)大家可以看到,go語(yǔ)言的包其實(shí)就是我們計(jì)算機(jī)里的目錄,或者叫文件夾,通過(guò)它們進(jìn)行目錄結(jié)構(gòu)和文件組織,go只是對(duì)目錄名字做了一個(gè)翻譯,叫【包】而已。比如這里的net包其實(shí)就是net目錄,http包其實(shí)就是http目錄,這也是go語(yǔ)言中的一個(gè)命名習(xí)慣,包名和文件所在的目錄名是一樣的。
包的命名
go語(yǔ)言的包的命名,遵循簡(jiǎn)潔、小寫、和go文件所在目錄同名的原則,這樣就便于我們引用,書(shū)寫以及快速定位查找。
比如go自帶的http這個(gè)包,它這個(gè)http目錄下的所有g(shù)o文件都屬于這個(gè)http包,所以我們使用http包里的函數(shù)、接口的時(shí)候,導(dǎo)入這個(gè)http包就可以了。
package?mainimport?"net/http"func?main()?{ ????http.ListenAndServe("127.0.0.1:80",handler); }
從這個(gè)例子可以看到,我們導(dǎo)入的是net/http,這在go里叫做全路徑,因?yàn)閔ttp包在net里面,net是最頂級(jí)的包,所以必須使用全路徑導(dǎo)入,go編譯程序才能找到http這個(gè)包,和我們文件系統(tǒng)的目錄路徑是一樣的。
因?yàn)橛辛巳窂剑悦陌梢院推渌麕?kù)的一樣,只要它們的全路徑不同就可以了,使用全路徑的導(dǎo)入,也增加了包名命名的靈活性。
對(duì)于自己或者公司開(kāi)發(fā)的程序而言,我們一般采用域名作為頂級(jí)包名的方式,這樣就不用擔(dān)心和其他開(kāi)發(fā)者包名重復(fù)的問(wèn)題了,比如我的個(gè)人域名是www.flysnow.org,那么我自己開(kāi)發(fā)的go程序都以flysnow.org作為全路徑中的最頂層部分,比如導(dǎo)入我開(kāi)發(fā)的一個(gè)工具包:
package?mainimport?"flysnow.org/tools"
如果你沒(méi)有自己的域名,怎么辦呢?這時(shí)候可以使用github.com。干研發(fā)這一行的,在github都會(huì)有個(gè)賬號(hào),如果沒(méi)有趕緊申請(qǐng)一個(gè),這時(shí)候我們就可以使用github.com/
package?mainimport?"github.com/rujews/tools"
這就是換成github.com命名的方式。
main包
當(dāng)把一個(gè)go文件的包名聲明為main時(shí),就等于告訴go編譯程序,我這個(gè)是一個(gè)可執(zhí)行的程序,那么go編譯程序就會(huì)嘗試把它編譯為一個(gè)二進(jìn)制的可執(zhí)行文件。
一個(gè)main的包,一定會(huì)包含一個(gè)main()函數(shù),這種我們也不陌生,比如C和Java都有main()函數(shù),它是一個(gè)程序的入口,沒(méi)這個(gè)函數(shù),程序就無(wú)法執(zhí)行。
在go語(yǔ)言里,同時(shí)要滿足main包和包含main()函數(shù),才會(huì)被編譯成一個(gè)可執(zhí)行文件。
我們看一個(gè)Hello World的Go語(yǔ)言版本,來(lái)說(shuō)明main 包。
package?mainimport?"fmt"func?main()?{ ????fmt.Println("Hello,?世界") }
假設(shè)該go文件叫hello.go,放在$GOPATH/src/hello目錄下,那么我們?cè)谶@個(gè)目錄下執(zhí)行g(shù)o build命令就會(huì)生成二進(jìn)制的可執(zhí)行文件,在window系統(tǒng)下生成的是hello.exe,在Unix,MAC和Linux下生成的是hello,我們?cè)贑MD或者終端里執(zhí)行它,就可以看到控制臺(tái)打印的:
Hello,?世界
二進(jìn)制可執(zhí)行文件的名字,就是該main包的go文件所在目錄的名字,因?yàn)閔ello.go在hello目錄下,所以生成的可執(zhí)行文件就是hello這個(gè)名字。
導(dǎo)入包
要想使用一個(gè)包,必須先導(dǎo)入它才可以使用,Go語(yǔ)言提供了import關(guān)鍵字來(lái)導(dǎo)入一個(gè)包,這個(gè)關(guān)鍵字告訴Go編譯器到磁盤的哪里去找要想導(dǎo)入的包,所以導(dǎo)入的包必須是一個(gè)全路徑的包,也就是包所在的位置。
import?"fmt"
這就表示我們導(dǎo)入了fmt包,也就等于告訴go編譯器,我們要使用這個(gè)包下面的代碼。如果要導(dǎo)入多個(gè)包怎么辦呢?Go語(yǔ)言還為我們提供的導(dǎo)入塊。
import?(????"net/http" ????"fmt")
使用一對(duì)括號(hào)包含的導(dǎo)入塊,每個(gè)包獨(dú)占一行。
對(duì)于多于一個(gè)路徑的包名,在代碼中引用的時(shí)候,使用全路徑最后一個(gè)包名作為引用的包名,比如net/http,我們?cè)诖a使用的是http,而不是net。
現(xiàn)在我導(dǎo)入了包,那么編譯的時(shí)候,go編譯器去什么位置找他們呢?這里就要介紹下Go的環(huán)境變量了。Go有兩個(gè)很重要的環(huán)境變量GOROOT和GOPATH,這是兩個(gè)定義路徑的環(huán)境變量,GOROOT是安裝Go的路徑,比如/usr/local/go;GOPATH是我們自己定義的開(kāi)發(fā)者個(gè)人的工作空間,比如/home/flysnow/go。
編譯器會(huì)使用我們?cè)O(shè)置的這兩個(gè)路徑,再加上import導(dǎo)入的相對(duì)全路徑來(lái)查找磁盤上的包,比如我們導(dǎo)入的fmt包,編譯器最終找到的是/usr/local/go/fmt這個(gè)位置。
值得了解的是:對(duì)于包的查找,是有優(yōu)先級(jí)的,編譯器會(huì)優(yōu)先在GOROOT里搜索,其次是GOPATH,一旦找到,就會(huì)馬上停止搜索。如果最終都沒(méi)找到,就報(bào)編譯異常了。
遠(yuǎn)程包導(dǎo)入
互聯(lián)網(wǎng)的時(shí)代,現(xiàn)在大家使用類似于Github共享代碼的越來(lái)越多,如果有的Go包共享在Github上,我們一樣有辦法使用他們,這就是遠(yuǎn)程導(dǎo)入包了,或者是網(wǎng)絡(luò)導(dǎo)入,Go天生就支持這種情況,所以我們可以很隨意的使用Github上的Go庫(kù)開(kāi)發(fā)程序。
import?"github.com/spf13/cobra"
這種導(dǎo)入,前提必須是該包托管在一個(gè)分布式的版本控制系統(tǒng)上,比如Github、Bitbucket等,并且是Public的權(quán)限,可以讓我們直接訪問(wèn)它們。
編譯在導(dǎo)入它們的時(shí)候,會(huì)先在GOPATH下搜索這個(gè)包,如果沒(méi)有找到,就會(huì)使用go get工具從版本控制系統(tǒng)(GitHub)獲取,并且會(huì)把獲取到的源代碼存儲(chǔ)在GOPATH目錄下對(duì)應(yīng)URL的目錄里,以供編譯使用。
go get工具可以遞歸獲取依賴包,如果github.com/spf13/cobra也引用了其他的遠(yuǎn)程包,該工具可以一并下載下來(lái)。
命名導(dǎo)入
我們知道,在使用import關(guān)鍵字導(dǎo)入包之后,我們就可以在代碼中通過(guò)包名使用該包下相應(yīng)的函數(shù)、接口等。如果我們導(dǎo)入的包名正好有重復(fù)的怎么辦呢?針對(duì)這種情況,Go語(yǔ)言可以讓我們對(duì)導(dǎo)入的包重新命名,這就是命名導(dǎo)入。
package?mainimport?(????"fmt" ????myfmt?"mylib/fmt")func?main()?{ ????fmt.Println() ????myfmt.Println() }
如果沒(méi)有重新命名,那么對(duì)于編譯器來(lái)說(shuō),這兩個(gè)fmt它是區(qū)分不清楚的。重命名也很簡(jiǎn)單,在我們導(dǎo)入的時(shí)候,在包名的左側(cè),起一個(gè)新的包名就可以了。
Go語(yǔ)言規(guī)定,導(dǎo)入的包必須要使用,否則會(huì)包編譯錯(cuò)誤,這是一個(gè)非常好的規(guī)則,因?yàn)檫@樣可以避免我們引用很多無(wú)用的代碼而導(dǎo)致的代碼臃腫和程序的龐大,因?yàn)楹芏鄷r(shí)候,我們都不知道哪些包是否使用,這在C和Java上會(huì)經(jīng)常遇到,有時(shí)候我們不得不借助工具來(lái)查找我們沒(méi)有使用的文件、類型、方法和變量等,把它們清理掉。
但是有時(shí)候,我們需要導(dǎo)入一個(gè)包,但是又不使用它,按照規(guī)則,這是不行的,為此Go語(yǔ)言給我們提供了一個(gè)空白標(biāo)志符_,只需要我們使用_重命名我們導(dǎo)入的包就可以了。
package?mainimport?( ????_?"mylib/fmt")
包的init函數(shù)
每個(gè)包都可以有任意多個(gè)init函數(shù),這些init函數(shù)都會(huì)在main函數(shù)之前執(zhí)行。init函數(shù)通常用來(lái)做初始化變量、設(shè)置包或者其他需要在程序執(zhí)行前的引導(dǎo)工作。比如上面我們講的需要使用_空標(biāo)志符來(lái)導(dǎo)入一個(gè)包的目的,就是想執(zhí)行這個(gè)包里的init函數(shù)。
我們以數(shù)據(jù)庫(kù)的驅(qū)動(dòng)為例,Go語(yǔ)言為了統(tǒng)一關(guān)于數(shù)據(jù)庫(kù)的訪問(wèn),使用databases/sql抽象了一層數(shù)據(jù)庫(kù)的操作,可以滿足我們操作MYSQL、Postgre等數(shù)據(jù)庫(kù),這樣不管我們使用這些數(shù)據(jù)庫(kù)的哪個(gè)驅(qū)動(dòng),編碼操作都是一樣的,想換驅(qū)動(dòng)的時(shí)候,就可以直接換掉,而不用修改具體的代碼。
這些數(shù)據(jù)庫(kù)驅(qū)動(dòng)的實(shí)現(xiàn),就是具體的,可以由任何人實(shí)現(xiàn)的,它的原理就是定義了init函數(shù),在程序運(yùn)行之前,把實(shí)現(xiàn)好的驅(qū)動(dòng)注冊(cè)到sql包里,這樣我們就使用使用它操作數(shù)據(jù)庫(kù)了。
package?mysqlimport?(????"database/sql")func?init()?{ ????sql.Register("mysql",?&MySQLDriver{}) }
因?yàn)槲覀冎皇窍雸?zhí)行這個(gè)mysql包的init方法,并不想使用這個(gè)包,所以我們?cè)趯?dǎo)入這個(gè)包的時(shí)候,需要使用_重命名包名,避免編譯錯(cuò)誤。
import?"database/sql"import?_?"github.com/go-sql-driver/mysql"db,?err?:=?sql.Open("mysql",?"user:password@/dbname")
看非常簡(jiǎn)潔,剩下針對(duì)的數(shù)據(jù)庫(kù)的操作,都是使用的database/sql標(biāo)準(zhǔn)接口,如果我們想換一個(gè)mysql的驅(qū)動(dòng)的話,只需要換個(gè)導(dǎo)入就可以了,靈活方便,這也是面向接口編程的便利。
這里關(guān)于Go包管理使用的就結(jié)束了,后面會(huì)接著講Go的一些常用工具、文檔以及依賴管理。歡迎大家關(guān)注公眾號(hào)[flysnow_org],敬請(qǐng)期待后續(xù)。
本文轉(zhuǎn)載自異步社區(qū)。
軟件開(kāi)發(fā) 編程語(yǔ)言
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。