iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 10
1
Software Development

下班加減學點Golang與Docker系列 第 10

Package 使用多個套件之前必須要懂的包

這次聊Package, 主要是因為接著都會需要對業務面向作模組的拆分.
或者對功能作拆分, 甚至是第三方套件的引入.
一個專案只要不是只有一個main.go, 就一定會有其他的package.

Package(包)

程式碼的目錄, 可以重複利用程式的方案, 方便維護。
Go默認提供很多package, 像是fmt、is等。
開發者也可以創建自己的package。

package要求所有檔案的第一行添加package名稱,標示該文件所歸屬的package。

package 包名稱
  • 一個目錄下的同級檔案屬於同一個package
  • package名稱可以與目錄不同名稱, 但盡可能一樣
  • main package為應用程式執行的entry point; 若沒有main package則無法編譯成可執行的檔案在bin下
  • package name, Go團隊建議簡單扁平為原則。 所以盡量避免下划線、中划線和參雜大寫字母。

Creating a package

  1. 可執行包(executable package)
    可自己執行,表示有main package
  2. 工具包(utility package)
    不可自己執行,但是可以給可執行包做擴展應用的作用

// main.go
package main

import (
	"fmt"
	. "hello/math"
)

func main() {
	fmt.Println("hello")
	fmt.Println(Average([]float64{1, 2}))
}
// math/math.go
package math

func Average(xs []float64) float64 {
	total := float64(0)
	for _, x := range xs {
		total += x
	}
	return total / float64(len(xs))
}
# 編譯hello package 
cd $GOPATH/src/hello; 
go install;
# 因為有main package, 所以會安裝到$GOPATH/bin 作為可執行包

# 編譯hello package 
cd $GOPATH/src/hello/math; 
go install;
# 因為沒有main package, 所以會安裝到$GOPATH/pkg下 作為工具包

Import package

使用import package,Go會先在 $GOROOT/src下尋找指定的package。
若找不到就往$GOPATH/src目錄下尋找。
找不到就會報出編譯錯誤。

package main

import (
  // fmt位於$GOROOT/src下,找到!
  "fmt"
  // gin並不在$GOROOT/src, 接著找$GOPATH/src找github.com這目錄,找到往內找gin-gonic目錄,再找gin package
  "github.com/gin-gonic/gin"
  // 
  . "github.com/go-sql-driver/mysql"
)

Nested package

在一個package內嵌套令一個package; 使用上只要指名路徑關係.

// math/math/extend/min.go
package extend

func init() {
	fmt.Println("extend ==> init()")
}

func Min(a float64, b float64) float64 {
	if a >= b {
		return a
	}
	return b
}
// math/math.go
package math

import (
	"fmt"
	"hello/math/extend"
)
func init() {
	fmt.Println("math ==> init()")
}

func Average(xs []float64) float64 {
	total := float64(0)
	for _, x := range xs {
		total += x
	}
	return total / float64(len(xs))
}

func Min(a float64, b float64) float64 {
	return extend.Min(a, b)
}
// main.go
package main

import (
	"fmt"
	. "hello/math"
)

func init() {
	fmt.Println("main ==> init()")
}

func main() {
	fmt.Println("hello")
	fmt.Println(Average([]float64{1, 2}))
	fmt.Println(Min(1, 2))
}

Package Initialization

工廠模式自動註冊-管理多個packge

// base/factory.go
package base

// define interface for Class
type Class interface {
	Do()
}

var (
	// 存放註冊好的 類別工廠資訊
	factoryByName = make(map[string]func() Class)
)

// 註冊一個類別工廠
func Register(name string, factory func() Class) {
	factoryByName[name] = factory
}

// 根據name創建對應的類別
func Create(name string) Class {
	if f, ok := factoryByName[name]; ok {
		return f()
	}
	panic("name not found")
}
// ex1/reg.go
package ex1

import (
	"fmt"
	"github.com/tedmax100/factory/base"
)
type Class1 struct {
}

func (c *Class1) Do() {
	fmt.Println("class1")
}

func init() {
	base.Register("Class1", func() base.Class {
		return new(Class1)
	})
}
// ex2/reg.go
package ex1

import (
	"fmt"

	"github.com/tedmax100/factory/base"
)

type Class2 struct {
}

func (c *Class2) Do() {
	fmt.Println("class2")
}

func init() {
	base.Register("Class2", func() base.Class {
		return new(Class2)
	})
}
// main.go
package main

import (
	"github.com/tedmax100/factory/base"
	_ "github.com/tedmax100/factory/ex1"
	_ "github.com/tedmax100/factory/ex2"
)
//因為上面使用匿名導入了ex1 & ex2 package.
//main()執行前, 這兩個package的init()會被調用, 而註冊了class1 & class2
func main() {
	c1 := base.Create("Class1")
	c1.Do()

上一篇
Interface & OOP 就說你是鴨子! 你就是要呱呱叫
下一篇
govendor 好多依賴要管理
系列文
下班加減學點Golang與Docker30

尚未有邦友留言

立即登入留言