[譯] part 7: golang 包

什麼是包以及爲何要使用它

到目前爲止,咱們看到的 go 代碼只有一個文件,其中有一個 main 函數和其餘幾個函數。在實際場景中,將全部源代碼寫入單個文件不是一個好方法。複用和維護這種代碼將變得很是艱難,包就是用來解決這些問題的。golang

包可使代碼更好的複用和可讀,也可使代碼解耦,所以使得應用程序很容易維護。windows

例如,假設咱們正在建立一個 go 圖像處理的應用程序,它提供了圖像裁剪,銳化,模糊和色彩加強等功能。組織此應用程序的一種方法是將與功能相關的全部代碼分組到本身的包中。例如,裁剪能夠是單個包,銳化能夠是另外一個包。這樣作的優勢是,色彩加強功能可能須要一些銳化功能。色彩加強代碼能夠簡單地導入(咱們將立刻討論包導入)銳化包並使用其功能。這樣代碼就變得易於複用。less

咱們將逐步建立一個計算矩形區域和對角線的應用程序,藉此應用程序讓咱們更好地瞭解包的做用。函數

main 函數和 main 包

每一個可執行的 go 應用程序都必須包含 main 函數。此函數是執行的入口。 main 函數應該放在 main 包中。ui

每一個 go 源代碼文件的第一行是package packagename,該語法用來指定該文件屬於哪一個包。spa

讓咱們開始爲咱們的應用程序建立 main 函數和 main 包。在 go 工做區的 src 文件夾中建立一個文件夾,並將其命名爲 geometry。在 geometry 文件夾中建立文件 geometry.go。日誌

geometry.go 的代碼以下,code

//geometry.go
package main 

import "fmt"

func main() {  
    fmt.Println("Geometrical shape properties")
}
複製代碼

代碼package main指定此文件屬於 main 包。import "packagename"語句用於導入現有的包。在該例子中,咱們導入了包含Println方法的 fmt 包。而後有一個 main 函數,打印了Geometrical shape properties教程

經過輸入go install geometry來編譯上面的程序。此命令在 geometry 文件夾中搜索具備 main 函數的文件。在該例子中,它會找到 geometry.go。而後編譯它並在工做區的bin文件夾內生成一個名爲 geometry 的二進制文件(在 windows 的狀況下爲 geometry.exe)。如今工做區結構將是開發

src  
    geometry
            gemometry.go
bin  
    geometry
複製代碼

咱們輸入workspacepath/bin/geometry來運行程序。將 workspacepath 替換爲 go 工做區的路徑。此命令執行 bin 文件夾中的 geometry 二進制文件,你能夠看到輸出爲Geometrical shape properties

建立自定義包

咱們以這樣的方式來組織代碼,讓與矩形相關的全部功能都在 rectangle 包中。

咱們來建立一個自定義包rectangle,它擁有計算矩形區域和對角線長度的兩個函數。

包的源文件應放在本身的單獨文件夾中,Go 中的一個約定是包的名稱和此文件夾的名稱相同。

所以,咱們在 geometry 文件夾中建立一個名爲 rectangle 的文件夾。rectangle 文件夾中的全部文件都應以package rectangle開頭,由於它們都屬於rectangle包。

在 rectangle 文件夾中建立一個 rectprops.go 文件,並添加如下代碼,

//rectprops.go
package rectangle

import "math"

func Area(len, wid float64) float64 {  
    area := len * wid
    return area
}

func Diagonal(len, wid float64) float64 {  
    diagonal := math.Sqrt((len * len) + (wid * wid))
    return diagonal
}
複製代碼

在上面的代碼中,咱們建立了AreaDiagonal函數。矩形的面積是長度和寬度的乘積,矩形的對角線長度是長度和寬度的平方和的平方根。math包中的Sqrt函數用於計算平方根。

注意,函數名稱AreaDiagonal都是大寫字母開頭,這是必要的,咱們待會解釋。

導入自定義包

要使用自定義包,必須先導入它。import path是導入自定義包的語法。咱們必須指定自定義包相對於工做空間內的 src 文件夾的路徑。咱們當前的文件夾結構是

src  
   geometry
           geometry.go
           rectangle
                    rectprops.go
複製代碼

import "geometry/rectangle" 是導入 rectangle 包的方式。

給 geometry.go 再添加些代碼,

//geometry.go
package main 

import (  
    "fmt"
    "geometry/rectangle" //importing custom package
)

func main() {  
    var rectLen, rectWidth float64 = 6, 7
    fmt.Println("Geometrical shape properties")
        /*Area function of rectangle package used */
    fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))
        /*Diagonal function of rectangle package used */
    fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}
複製代碼

上面的代碼導入了 rectangle 包,並使用它的AreaDiagonal函數來計算矩形面積和對角線長度。Printf中的%.2f格式化符號是將浮點數截取爲兩位小數。該程序的輸出是

Geometrical shape properties  
area of rectangle 42.00  
diagonal of the rectangle 9.22  
複製代碼

可導出命名

將 rectangle 包中的函數AreaDiagonal首字母大寫,這在 Go 中有特殊意義。任何以大寫字母開頭的變量或函數都是 go 中的可導出命名,用這種命名方式的函數和變量可以被其餘包訪問。在該例子中,咱們經過這種方式在 main 包中了訪問AreaDiagonal行數。

若是把 rectprops.go 的函數名由Area(len, wid float64)更改成area(len, wid float64),並在 geometry.go 中由rectangle.Area(rectLen, rectWidth)更改成rectangle.area(rectLen, rectWidth)。若是程序運行,編譯器將拋出錯誤geometry.go:11: cannot refer to unexported name rectangle.area。所以,若是要訪問其餘包的函數,則應使用可導出的命名方式。

init 函數

每一個包均可以包含 init 函數。init 函數不該該有任何返回類型,也不該該有任何參數,在咱們的源代碼中也沒法顯式調用 init 函數。 init 函數以下所示

func init() {  
}
複製代碼

init 函數用於執行初始化任務,也可用於在執行開始以前驗證程序的正確性。

包的初始化順序以下

  1. 首先初始化包級別變量
  2. 接下來調用 init 函數。一個包能夠有多個 init 函數(在單個文件中或分佈在多個文件中),並按照它們呈現給編譯器的順序調用它們。

若是包導入其餘包,則首先初始化該導入的包。

即便從多個包導入同一個包,被導入的包也只會初始化一次。

讓咱們對咱們的應用程序進行一些修改以理解 init 函數。

首先,咱們將一個 init 函數添加到 rectprops.go 文件中。

//rectprops.go
package rectangle

import "math"  
import "fmt"

/* * init function added */
func init() {  
    fmt.Println("rectangle package initialized")
}
func Area(len, wid float64) float64 {  
    area := len * wid
    return area
}

func Diagonal(len, wid float64) float64 {  
    diagonal := math.Sqrt((len * len) + (wid * wid))
    return diagonal
}
複製代碼

咱們添加了一個簡單的 init 函數,它只打印rectangle package initialised

如今來修改一下 main 包,衆所周知矩形的長度和寬度應該大於零。咱們可使用 geometry.go 文件中的 init 函數和包級別變量來定義此檢查。

將 geometry.go 修改成以下,

//geometry.go
package main 

import (  
    "fmt"
    "geometry/rectangle" //importing custom package
    "log"
)
/* * 1. package variables */
var rectLen, rectWidth float64 = 6, 7 

/* *2. init function to check if length and width are greater than zero */
func init() {  
    println("main package initialized")
    if rectLen < 0 {
        log.Fatal("length is less than zero")
    }
    if rectWidth < 0 {
        log.Fatal("width is less than zero")
    }
}

func main() {  
    fmt.Println("Geometrical shape properties")
    fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))
    fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}
複製代碼

如下是對 geometry.go 所作的更改

  1. rectLenrectWidth變量從 main 函數級別移動到包級別。
  2. 添加了一個 init 函數。若是rectLenrectWidth小於零則使用log.Fatal函數打印日誌並終止。

main 包的初始化順序是

  1. 首先初始化導入的包。所以 rectangle 包首先被初始化
  2. 接下來初始化包級別變量rectLenrectWidth
  3. 調用 init 函數
  4. 最後調用 main 函數

若是運行程序,將輸出

rectangle package initialized  
main package initialized  
Geometrical shape properties  
area of rectangle 42.00  
diagonal of the rectangle 9.22  
複製代碼

正如所料,首先調用 rectangle 包的 init 函數,而後初始化包級變量rectLenrectWidth,接下來調用 main 包的 init 函數,它檢查rectLenrectWidth是否小於零,若是小於零則打印日誌並終止。咱們會在單獨的教程中詳細瞭解 if 語句。如今你能夠假設若是rectLen < 0就是判斷rectLen是否小於 0,若是是,則程序將被終止。咱們爲rectWidth也寫了同樣的條件。在該例子中,兩個條件都爲false,程序繼續執行。最後調用 main 函數。

再稍微修改一下該程序,以瞭解 init 函數的用法。

將 geometry.go 的var rectLen, rectWidth float64 = 6, 7更改成var rectLen, rectWidth float64 = -6, 7rectLen被初始化爲負數。

修改後運行會輸出,

rectangle package initialized  
main package initialized  
2017/04/04 00:28:20 length is less than zero  
複製代碼

正常狀況下,先初始化 rectangle 包,而後是 main 包中的包級別變量rectLenrectWidthrectLen是負數。所以,當 init 函數運行時,程序在打印length is less than zero後終止。

使用_符號

Go 中導入包而且不在代碼中的任何位置使用它是非法的。若是你這樣作,編譯器會報錯。這樣作的緣由是爲了不大量未使用的包顯著增長編譯時間。用如下代碼替換 geometry.go 中的代碼,

//geometry.go
package main 

import (   

     "geometry/rectangle" //importing custom package

)
func main() {

}
複製代碼

上面的程序將拋出錯誤geometry.go:6: imported and not used: "geometry/rectangle"

可是,當應用程序處於開發狀態時導入各類包很常見,可能接下來不必定要使用這個包。 _符號能夠用於這些場景。

如下代碼可使上述程序中正常運行,

package main

import (  
    "geometry/rectangle" 
)

var _ = rectangle.Area //error silencer

func main() {

}
複製代碼

該行var _ = rectangle.Area會使程序不會報錯。若是咱們不使用這個包,咱們應該關注這些代碼,並在應用程序開發結束時刪除它們。所以,在程序開發階段,能夠在 import 語句以後用該方式避免報錯。(譯者注:通常用_存儲不使用的變量等等,畢竟聲明瞭若是不使用編譯器會報錯的)

有時咱們導入一個包只是爲了使用初始化功能,除此以外咱們不須要使用包中的任何函數或變量。例如,咱們可能須要調用 rectangle 包的 init 函數,即便在代碼中的任何位置都不使用該包。在這種狀況下也可使用_符號,以下所示。

package main 

import (   

     _ "geometry/rectangle" 

)
func main() {

}
複製代碼

運行上面的程序將輸出rectangle package initialized。咱們已經成功初始化了包,即便它沒有在代碼中的任何地方被使用。(譯者注:包導入還有.操做能夠省略前綴包名直接調用該包的函數或者變量。別名操做也比較好理解,就是把前綴包名重命名,語法是 import alias package

相關文章
相關標籤/搜索