Go – Package

Trong thuật ngữ cơ bản, Package không gì ngoài một thư mục bên trong Workspace của Go chứa một hay nhiều file code Go, hoặc các Package khác.

Tất cả các file code của Go đều gắn với một Package. Để khai báo một file code như là một phần của Package sử dụng cú pháp như sau

package <packagename>

Khai báo Package trên phải được đặt ở dòng đầu tiên của code. Tất cả các hàm, các kiểu, và các biến được định nghĩa trong file Souce Code của Go và trở thành một phần của Package.

Bạn có thể lựa chọn Export một thành viên được định nghĩa trong Package cho các Package bên ngoài, hoặc giữ chúng private trong cùng một Package. Các Package có thể Import và sử dụng lại được các hàm, các kiểu mà được Export từ Package.

Hãy cùng xem ví dụ sau. Hầu hết các ví dụ từ trước đến giờ của chúng ta đều sử dụng dòng này

import "fmt"

fpm là Package thư viện cốt lõi chứa các chứ năng liên quan đến định dạng và in kết quả hoặc đọc các đầu vào từ các nguồn I/O khác nhau. Nó Export các hàm như là Println()Printf()Scanf() cho các Package khác sử dụng.

Ưu điểm

  • Giảm sự nhầm lẫn trong việc đặt tên. Bạn có thể có các hàm trùng tên trong các Package khác nhau. Điều này giúp cho tên của hàm ngắn gọn và dễ hiểu.
  • Tổ chức các code liên quan đến nhau giúp dễ dàng hơn trong việc tìm kiếm các code mà bạn muốn sử dụng lại
  • Tăng tốc độ quá trình biên dịch bằng cách chỉ thực hiện biên dịch lại các phần nhỏ hơn của chương trình mà chắc chắn thay đổi. Mặc dù chúng ta sử dụng Package fmt nhưng chúng ta không cần biên dịch lại nó mỗi khi chương trình thay đổi.

Package main và hàm main()

Các chương trình Go bắt đầu chạy trong Package main. Nó là một Package đặc biệt được chương trình sử dụng với ý nghĩa là có thể thực thi được.

Theo quy ước, các chương trình có thể thực thi được gọi là các Command. Ngoài ra gọi đơn giản là các Package.

Hàm main() là một hàm đặc biệt là điểm vào để chạy chương trình. Hãy xem ví dụ sau về cách chạy một chương trình trong Go.

// Khai báo Package
package main

// thêm các Package
import (
	"fmt"
	"time"
	"math"
    "math/rand"
)

func main() {
	// tìm số lớn nhất
	fmt.Println(math.Max(73.15, 92.46))
	
	// Tính căn bậc 2
	fmt.Println(math.Sqrt(225))

	// in ra gia trị của ``
	fmt.Println(math.Pi)

	epoch := time.Now().Unix()
	fmt.Println(epoch)

	// tạo số nguyên ngẫu nhiên trong khoảng từ 0 đến 100
	rand.Seed(epoch)
	fmt.Println(rand.Intn(100))
}
$ go run main.go

Kết quả

92.46
15
3.141592653589793
1538045386
40

Import các Package

Có 2 cách để Import các Package trong Go

1. Import trong từng câu lệnh

import "fmt"
import "time"
import "math"
import "math/rand"

2. Import gộp trong một câu lệnh

import (
	"fmt"
	"time"
	"math"
        "math/rand"
)

Quy ước của Go là – Tên Package là phần tử cuối cùng của đường dẫn ví dụ như tên của Package được Import math/randrand. Nó được Import sử dụng math/rand bởi vì nó là thư mục con của Package math

Quy ước tên được Export và không được Export

Tất cả tên (biến, kiểu, hay hàm) bắt đầu với ký tự viết hoa thì Export được và các Package bên ngoài có thể nhìn thấy được.

Tất cả tên không bắt đầu với ký tự viết hoa thì không được Export và chỉ được nhìn thấy trong cùng Package.

Khi Import các Package, chúng ta chỉ truy cập đến các tên được Export (viết hoa ký tự đầu tiên)

package main

import (
	"fmt"
	"math"
)

func main() {
	// MaxInt64 là tên được Export
	fmt.Println("Max value of int64: ", int64(math.MaxInt64))

	// Phi là tên được Export
	fmt.Println("Value of Phi (ϕ): ", math.Phi)

	// pi bắt đầu với ký tự viết thường, nên không được Export
	fmt.Println("Value of Pi (): ", math.pi)
}

Kết quả

./exported_names.go:16:38: cannot refer to unexported name math.pi
./exported_names.go:16:38: undefined: math.pi

Để sửa lỗi trên, bạn cần đổi pi thành Pi

Tạo và quản lý các Package tuỳ chỉnh

Hiện tại, chúng ta mới chỉ viết code trong Package main và sử dụng các chức năng được Import từ các Package thư viện của Go.

Chúng ta hãy cùng tạo một Project Go mẫu mà có nhiều Package tuỳ chỉnh và cùng tìm hiểu cách khai báo, Import và Export các Package tuỳ chỉnh.

Đây là cấu trúc của Go project

Thư mục src chứa Source Code của chúng ta bên trong thư mục example. Thư mục binpkg chứa binary chạy và lưu trữ các Package tương ứng. Chúng sẽ được tự động sinh ra bởi công cụ Go khi cài đặt chương trình

Hãy cùng nhìn toàn bộ Source Code bên trong Project

numbers/prime.go

package numbers

import "math"

// kiểm tra xem có phải là số nguyên tố hay không
func IsPrime(num int) bool {
    for i := 2; i <= int(math.Floor(math.Sqrt(float64(num)))); i++ {
        if num%i == 0 {
            return false
        }
	}
    return num > 1
}

strings/reverse.go

package strings

// Reverses a string
/*
	Vì String trong Go là immutable, nên chúng ta chuyển sang thành array mutable 
	thực hiện đảo ngược, và sao đó tạo thành chuỗi string.
*/
func Reverse(s string) string {
	runes := []rune(s)
	reversedRunes := reverseRunes(runes)
	return string(reversedRunes)
}

strings/reverse_runes.go

package strings

// đảo ngược mảng runes
// Hàm này không Export (nó chỉ nhìn thấy được trong `strings` package)
func reverseRunes(r []rune) []rune {
	for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return r
}

strings/greeting/texts.go

// Nested Package
package greeting

// được Export
const  (
	WelcomeText = "Hello, World to Golang"
    MorningText = "Good Morning"
	EveningText = "Good Evening"
)

// Không được Export (chỉ nhìn thấy trong `greeting` package)
var loremIpsumText = `very, very long text`

myapp/app.go 

package main

import (
	"fmt"
	"github.com/callicoder/example/numbers"
	"github.com/callicoder/example/strings"	
	"github.com/callicoder/example/strings/greeting" // Importing a nested package
	str "strings"	// Package Alias
)

func main() {
	fmt.Println(numbers.IsPrime(19))

	fmt.Println(greeting.WelcomeText)

	fmt.Println(strings.Reverse("callicoder"))

	fmt.Println(str.Count("Go is Awesome. I love Go", "Go"))
}
# chạy binary
$ myapp
true
Hello, World to Golang
redocillac
2

Sau đây là những điểm cần chú ý:

  • Đường dẫn Import:  Tất cả các đường dẫn Import đều thuộc về các thư mục trong src
    import (
        "github.com/callicoder/example/numbers"
        "github.com/callicoder/example/strings" 
        "github.com/callicoder/example/strings/greeting"
    )
  • Package Alias: Chúng ta sử dụng các Package Alias để giải quyết xung đột giữa các Package khác nhau mà trùng tên, hoặc gán cho một tên ngắn gọn để import Package.
    import (
        str "strings"   // Package Alias
    )
  • src
        github.com/callicoder/example
            strings                     # Package
                greeting                # Nested Package
                    texts.go

    Một Nested Package có thể được Import giống như Package gốc. Chỉ cần cung cấp đường dẫn của nó liên quan đến thư mục $GOPATH/src

  • Nested Package: Bạn có thể lồng Package vào trong Package khác. Nó đơn giản là tạo thư mục con.
    import (
        "github.com/callicoder/example/strings/greeting"
    )

 Cài đặt Package bên thứ 3

Bạn có thể sử dụng câu lệnh go get để lấy các Package bên thứ 3 từ các Repository trên mạng

$ go get -u github.com/jinzhu/gorm

Câu lệnh trên lấy gorm Package từ Github và lưu nó trong đường dẫn src/github.com/jinzhu/gorm trong thư mục Project.

Tiếp theo bạn có thể Import và sử dụng Package trên như sau

import "github.com/jinzhu/gorm"

Không giống các ngôn ngữ khác và các công cụ quản lý Package như npm, maven… Go không có một nơi lưu trữ tập trung chính thức, bởi vậy nó sẽ yêu cầu bạn cung cấp hostname và đường dẫn Package trong câu lệnh go get.

 

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.