Package trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu package là gì, cách tạo và quản lý package, cách tạo module, import package, ... trong Go.

Package là gì?

Cho đến nay, chúng ta đã thấy các chương trình Go chỉ có một tệp với một hàm main và một vài hàm khác. Trong các tình huống thực tế, cách tiếp cận này để viết tất cả mã nguồn trong một tập tin duy nhất là không thể mở rộng. Không thể sử dụng lại và duy trì mã được viết theo cách này. Đây là nơi package (gói) hữu ích.

Package được sử dụng để tổ chức mã nguồn Go để có thể tái sử dụng và dễ đọc hơn. Package là một tập hợp các tập tin mã nguồn Go nằm trong cùng một thư mục. Package cung cấp phân vùng mã và do đó, việc duy trì các dự án Go trở nên dễ dàng.

Ví dụ: giả sử chúng ta đang viết một ứng dụng tài chính trong Go và một số chức năng là tính lãi đơn giản, tính lãi kép và tính khoản vay. Một cách đơn giản để tổ chức ứng dụng này là theo chức năng.

Chúng ta có thể tạo ra các package là simpleInterest, compoundInterestloan. Nếu package loan cần tính lãi đơn giản, nó có thể làm điều đó bằng cách import package simpleInterest. Bằng cách này, mã được tái sử dụng.

Chúng ta sẽ tìm hiểu package bằng cách tạo một ứng dụng đơn giản để xác định lãi suất đơn giản cho gốc, lãi suất và thời gian tính theo năm.

Function main và Package main

Mọi ứng dụng Go có thể thực thi phải chứa function main. Function này là điểm đầu vào để thực thi. Function main nên nằm trong .

package packagename chỉ định rằng một tập tin mã nguồn cụ thể thuộc về package packagename. Đây phải là dòng đầu tiên của mọi tập tin mã nguồn Go.

Hãy bắt đầu bằng cách tạo function main và package main cho ứng dụng của chúng ta.

Chạy lệnh dưới đây để tạo một thư mục có tên learnpackage bên trong thư mục Documents của người dùng hiện tại.

mkdir ~/Documents/learnpackage/  

Tạo một tệp có tên main.go bên trong thư mục learnpackage của chúng ta với nội dung sau.

package main 

import "fmt"

func main() {  
    fmt.Println("Simple interest calculation")
}

Dòng mã package main chỉ định rằng tập tin này thuộc về package main. Câu lệnh import "packagename" được sử dụng để import một package. Câu lệnh packagename.FunctionName() là cú pháp để gọi một function trong một package.

Trong dòng số 3, chúng ta import package fmt để sử dụng function Println. fmt là một package tiêu chuẩn và có sẵn như một phần của thư viện tiêu chuẩn Go. Sau đó, có function main sẽ in ra dòng Simple interest calculation

Biên dịch chương trình trên bằng cách di chuyển đến thư mục learnpackage sử dụng lệnh:

cd ~/Documents/learnpackage/  

và gõ lệnh sau

go install  

Nếu mọi việc suôn sẻ, tập tin nhị phân của chúng ta sẽ được biên dịch và sẵn sàng để thực thi. Gõ lệnh learnpackage vào terminal và bạn sẽ thấy kết quả sau.

Simple interest calculation  

Nếu bạn không hiểu cách lệnh go install hoạt động hoặc nếu bạn gặp lỗi

go install: no install location for directory /home/naveen/Documents/learnpackage outside GOPATH  
For more details see: 'go help gopath'  

vui lòng truy cập bài viết hướng dẫn cách chạy một chương trình Go để biết thêm.

Module Go

Chúng ta sẽ cấu trúc mã theo cách mà tất cả các chức năng liên quan đến tính lãi đơn giản đều nằm trong package simpleInterest. Để làm điều đó, chúng ta cần tạo một package tùy chỉnh simpleInterest chứa function để tính lãi đơn giản. Trước khi tạo package tùy chỉnh, chúng ta cần hiểu Module Go trước, vì nó cần thiết để tạo các package tùy chỉnh.

Module Go không là gì ngoài một tập hợp các package Go. Bây giờ câu hỏi này có thể xuất hiện trong đầu bạn. Tại sao chúng ta cần module Go để tạo một package tùy chỉnh?

Câu trả lời là đường dẫn import cho package tùy chỉnh mà chúng ta tạo được lấy từ tên của module Go. Ngoài ra, tất cả các package bên thứ ba khác (chẳng hạn như mã nguồn từ github) mà ứng dụng của chúng ta sử dụng sẽ có trong tập tin go.mod cùng với phiên bản. Tập tingo.mod được tạo ra khi chúng ta tạo ra một module mới. Bạn sẽ hiểu rõ hơn điều này trong phần tiếp theo.

Một câu hỏi khác có thể xuất hiện trong đầu chúng ta. Tại sao từ đầu tới giờ chúng ta lại không tạo module Go? Câu trả lời là, chúng ta chưa bao giờ tạo package tùy chỉnh của riêng mình cho đến bây giờ trong loạt bài hướng dẫn này và do đó không cần tạo module Go.

Lý thuyết đủ rồi :). Hãy bắt tay vào hành động và tạo module Go và package tùy chỉnh của chúng ta.

Tạo module Go

Đảm bảo rằng bạn đang ở trong thư mục learnpackage bằng cách gõ lệnh cd ~/Documents/learnpackage/. Bên trong thư mục này, hãy chạy lệnh sau để tạo một module Go có tên là learningpackage .

go mod init learnpackage  

Lệnh trên sẽ tạo một tệp có tên go.mod. Sau đây sẽ là nội dung của tập tin.

module learnpackage

go 1.13  

Dòng module learnpackage chỉ định rằng tên của module là learnpackage. Như chúng ta đã đề cập trước đó, learnpackage sẽ là đường dẫn cơ sở để nhập bất kỳ gói nào được tạo bên trong module này. Dòng go 1.13 chỉ định rằng các tệp trong module này sử dụng phiên bản go 1.13.

Tạo package simpleInterest

Các tập tin mã nguồn thuộc một package nên được đặt trong thư mục riêng biệt của chúng. Một quy ước trong Go đặt tên thư mục này cùng tên với package.

Hãy tạo một thư mục có tên simpleInterest bên trong thư mục learnpackage. Lệnh mkdir simpleInterest sẽ tạo thư mục này cho chúng ta.

Tất cả các tập tin bên trong thư mục simpleInterest phải bắt đầu bằng dòng package simpleInterest vì tất cả chúng đều thuộc về package simpleInterest.

Tạo một tập tin simpleInterest.go bên trong thư mục simpleInterest.

Sau đây sẽ là cấu trúc thư mục của ứng dụng của chúng ta.

├── learnpackage
│   ├── go.mod
│   ├── main.go
│   └── simpleInterest
│       └── simpleInterest.go

Thêm mã sau vào tập tin simpleInterest.go.

package simpleInterest

//Calculate calculates and returns the simple interest for a principal p, rate of interest r for time duration t years
func Calculate(p float64, r float64, t float64) float64 {  
    interest := p * (r / 100) * t
    return interest
}

Trong đoạn mã trên, chúng ta đã tạo một function Calculate tính toán và trả về tiền lãi đơn giản.

Lưu ý rằng tên function Calculator bắt đầu bằng chữ hoa. Đây là điều cần thiết và chúng tôi sẽ giải thích ngay sau đây tại sao lại cần điều này.

Import package tùy chỉnh

Để sử dụng một package tùy chỉnh, trước tiên chúng ta phải import nó. Đường dẫn import là tên của module được thêm vào bởi thư mục con của package và tên package. Trong trường hợp của chúng ta, tên module là learnpackage và tên package là simpleInterest nằm trong thư mục simpleInterest trực tiếp dưới thư mục learnpackage.

├── learnpackage
│   └── simpleinterest

Vì vậy, dòng import "learnpackage/simpleInterest" sẽ import package simpleInterest.

Trong trường hợp chúng ta có cấu trúc thư mục như thế này

learnpackage  
│   └── finance
│       └── simpleinterest 

thì câu lệnh import sẽ là import "learnpackage/finance/simpleInterest"

Thêm mã sau vào tập tin main.go

package main

import (  
    "fmt"
    "learnpackage/simpleInterest"
)

func main() {  
    fmt.Println("Simple interest calculation")
    p := 5000.0
    r := 10.0
    t := 1.0
    si := simpleInterest.Calculate(p, r, t)
    fmt.Println("Simple interest is", si)
}

Đoạn mã trên import package simpleinterest và sử dụng function Calculate để tính lãi suất đơn giản. Các gói trong thư viện chuẩn không cần tiền tố tên module và do đó "fmt" hoạt động mà không cần tiền tố module. Khi ứng dụng chạy sẽ in ra kết quả sau:

Simple interest calculation  
Simple interest is 500  

Hiểu thêm về lệnh go install

Bây giờ chúng ta đã hiểu cách hoạt động của package, đã đến lúc nói thêm một chút về lệnh go install. Các công cụ Go giống như lệnh go install làm việc trong bối cảnh của thư mục hiện tại. Hãy hiểu điều đó có nghĩa là gì. Cho đến bây giờ chúng ta đã chạy lệnh go install từ thư mục ~/Documents/learnpackage/. Nếu chúng ta cố gắng chạy nó từ bất kỳ thư mục nào khác, nó sẽ không thành công.

Hãy thử viết mã vào cd ~/Documents/ và sau đó chạy go install learnpackage. Nó sẽ không thành công với lỗi sau.

can't load package: package learnpackage: cannot find package "learnpackage" in any of:  
    /usr/local/Cellar/go/1.13.7/libexec/src/learnpackage (from $GOROOT)
    /Users/nramanathan/go/src/learnpackage (from $GOPATH)

Hãy hiểu lý do đằng sau lỗi này. Lệnh go install lấy tên package tùy chỉnh làm tham số (trong trường hợp của chúng ta là tên gói learnpackage) và nó cố gắng biên dịch function main nếu package tồn tại trong thư mục hiện tại mà từ đó nó được chạy hoặc trong thư mục cha của nó, v.v. .

Chúng ta đang ở trong thư mục Documents và không có tập tin go.mod nào ở đó và do đó lệnh go install phàn nàn rằng nó không thể tìm thấy package learnpackage.

Khi chúng ta chuyển đến thư mục ~/Documents/learnpackage/, có một tập tin go.mod ở đó và tên module learnpackage của chúng ta nằm trong tập tin go.mod đó.

Vì vậy lệnh go install learnpackage sẽ hoạt động từ bên trong thư mục ~/Documents/learnpackage/.

Nhưng cho đến nay chúng ta mới sử dụng lệnh go install và chúng ta không chỉ định tên package. Nếu không có tên package nào được chỉ định, lệnh go install sẽ mặc định là tên module trong thư mục làm việc hiện tại. Đó là lý do tại sao khi lệnh go install vẫn chạy mà không có bất kỳ tên package nào từ thư mục ~/Documents/learnpackage/ nó hoạt động. Vì vậy, 3 lệnh sau đây là tương đương khi chạy từ thư mục ~/Documents/learnpackage/

go install

go install .

go install learnpackage  

Tôi cũng đã đề cập rằng lệnh go install có khả năng tìm kiếm đệ quy trong thư mục cha cho tập tin go.mod. Hãy kiểm tra xem điều đó có hiệu quả không.

cd ~/Documents/learnpackage/simpleInterest/  

Lệnh trên sẽ đưa chúng ta đến thư mục simpleInterest. Từ thư mục đó chạy lệnh

go install learnpackage  

Đi cài đặt thành công sẽ tìm thấy một file go.mod trong thư mục cha learnpackage có module learnpackage được xác định và do đó nó hoạt động :).

Export tên function

Chúng ta viết hoa function Calculate trong package simpleInterest. Điều này có một ý nghĩa đặc biệt trong Go. Bất kỳ biến hoặc function nào bắt đầu bằng một chữ cái viết hoa đều là tên được export.

Chỉ các function và biến đã export mới có thể được truy cập từ các package khác. Trong trường hợp của chúng ta, chúng ta muốn truy cập function Calculate từ package main. Do đó, tên function này được viết hoa.

Nếu tên function được thay đổi từ Calculate thành calculate ở tập tin simpleinterest.go, và nếu chúng ta cố gắng gọi function sử dụng lệnh simpleinterest.calculate(p, r, t) trong tập tin main.go, trình biên dịch sẽ báo lỗi

# learnpackage
./main.go:13:8: cannot refer to unexported name simpleinterest.calculate
./main.go:13:8: undefined: simpleinterest.calculate

Do đó, nếu bạn muốn truy cập vào một function bên ngoài một package, nó phải được viết hoa.

Function init

Mỗi package trong Go có thể chứa một function init. Function init không có bất kỳ kiểu trả về và không có bất kỳ tham số đầu vào nào. Function init không thể được gọi một cách rõ ràng trong mã nguồn của chúng ta. Nó sẽ được gọi tự động khi package được khởi tạo. Function init có cú pháp sau

func init() {  
}

Function init có thể được sử dụng để thực hiện nhiệm vụ khởi tạo và cũng có thể được sử dụng để xác minh tính đúng đắn của chương trình trước khi thực hiện khởi động.

Thứ tự khởi tạo một package như sau

  1. Các biến ở cấp package được khởi tạo đầu tiên
  2. Function init được gọi là tiếp theo. Một package có thể có nhiều hàm init (trong một tập tin duy nhất hoặc được phân phối trên nhiều tập tin) và chúng được gọi theo thứ tự được trình bày cho trình biên dịch.

Nếu một package import các package khác, thì các package đã import sẽ được khởi tạo trước.

Một package sẽ chỉ được khởi tạo một lần ngay cả khi nó được import từ nhiều package.

Hãy thực hiện một số sửa đổi đối với ứng dụng của chúng ta để hiểu function init.

Để bắt đầu, hãy thêm function init vào tập tin simpleInterest.go.

package simpleInterest

import "fmt"

/*
 * init function added
 */
func init() {  
    fmt.Println("Simple interest package initialized")
}
//Calculate calculates and returns the simple interest for principal p, rate of interest r for time duration t years
func Calculate(p float64, r float64, t float64) float64 {  
    interest := p * (r / 100) * t
    return interest
}

Chúng ta đã thêm function init đơn giản chỉ in ra dòng Simple interest package initialised

Bây giờ chúng ta hãy sửa đổi package main. Chúng ta biết rằng gốc, lãi suất và thời hạn phải lớn hơn 0 khi tính lãi đơn giản. Chúng ta sẽ xác định kiểm tra này bằng cách sử dụng hàm init và các biến ở cấp package trong tập tin main.go.

Sửa đổi tập tin main.go như sau,

package main 

import (  
    "fmt"
    "learnpackage/simpleInterest" //importing custom package
    "log"
)
var p, r, t = 5000.0, 10.0, 1.0

/*
* init function to check if p, r and t are greater than zero
 */
func init() {  
    println("Main package initialized")
    if p < 0 {
        log.Fatal("Principal is less than zero")
    }
    if r < 0 {
        log.Fatal("Rate of interest is less than zero")
    }
    if t < 0 {
        log.Fatal("Duration is less than zero")
    }
}

func main() {  
    fmt.Println("Simple interest calculation")
    si := simpleInterest.Calculate(p, r, t)
    fmt.Println("Simple interest is", si)
}

Sau đây là những thay đổi được thực hiện đối với tập tin main.go

  1. Các biến p , rt được chuyển từ cấp function main lên cấp package.
  2. Function init đã được thêm vào. Function init in ra nhật ký và kết thúc việc thực thi chương trình nếu tiền gốc, lãi suất hoặc khoảng thời gian nhỏ hơn 0 bằng cách sử dụng function log.Fatal.

Thứ tự khởi tạo như sau,

  1. Các package đã import được khởi tạo trước. Do đó, package simpleInterest được khởi tạo đầu tiên và function init của nó được gọi.
  2. Các biến mức package là p , rt được khởi tạo tiếp theo.
  3. Function init được gọi trong package main.
  4. Function main được gọi sau cùng.

Nếu bạn chạy chương trình, bạn sẽ nhận được kết quả sau.

Simple interest package initialized  
Main package initialized  
Simple interest calculation  
Simple interest is 500  

Như mong đợi, function init của package simpleInterest được gọi đầu tiên, tiếp theo là khởi tạo các biến mức package p, rt. Function init của package main được gọi tiếp theo. Nó kiểm tra xem các biến p, rt nhỏ hơn 0 và chấm dứt nếu điều kiện là đúng.

Chúng ta sẽ tìm hiểu chi tiết về câu lệnh if trong một hướng dẫn riêng. Bây giờ, bạn có thể giả sử rằng lệnh if p < 0 sẽ kiểm tra xem p có nhỏ hơn 0 hay không và nếu có, chương trình sẽ bị kết thúc.

Chúng ta đã viết một điều kiện tương tự cho rt. Trong trường hợp này, tất cả các điều kiện này đều sai và việc thực thi chương trình vẫn tiếp tục. Cuối cùng, function main được gọi.

Hãy sửa đổi chương trình này một chút để tìm hiểu cách sử dụng function init.

Thay đổi dòng

var p, r, t = 5000.0, 10.0, 1.0  

trong main.go thành

var p, r, t = -5000.0, 10.0, 1.0  

Chúng ta đã khởi tạo giá trị của biến p thành âm.

Bây giờ nếu bạn chạy ứng dụng, bạn sẽ thấy

Simple interest package initialized  
Main package initialized  
2020/02/15 21:25:12 Principal is less than zero  

Biến p là âm. Do đó, khi chạy function init, chương trình sẽ kết thúc sau khi in ra dòng Principal is less than zero.

Sử dụng mã định danh trống

Việc import một package và không sử dụng package đó ở bất kỳ đâu trong mã là bất hợp pháp trong Go. Trình biên dịch sẽ phàn nàn nếu bạn làm như vậy. Lý do cho điều này là để tránh các package không sử dụng sẽ làm tăng đáng kể thời gian biên dịch. Thay thế mã trong tập tin main.go bằng mã sau,

package main

import (  
        "learnpackage/simpleInterest"
)

func main() {

}

Chương trình trên sẽ bị lỗi

# learnpackage
./main.go:4:2: imported and not used: "learnpackage/simpleinterest"

Điều này khá phổ biến, import các package khi ứng dụng đang được phát triển tích cực và sử dụng chúng ở đâu đó trong mã sau này nhưng không phải bây giờ. Mã định danh trống _ sẽ cứu chúng ta trong những tình huống đó.

Lỗi trong chương trình trên có thể được tắt bằng mã sau,

package main

import (  
        "learnpackage/simpleInterest"
)

var _ = simpleInterest.Calculate

func main() {

}

Dòng lệnh var _ = simpleinterest.Calculate sẽ tắt thông báo lỗi. Chúng ta nên theo dõi kỹ các loại giảm lỗi này và loại bỏ chúng bao gồm cả package đã import khi kết thúc phát triển ứng dụng nếu package đó không được sử dụng. Do đó, bạn nên viết loại giảm lỗi ở cấp độ package ngay sau câu lệnh import.

Đôi khi chúng ta cần import một package chỉ để đảm bảo quá trình khởi tạo diễn ra mặc dù chúng ta không cần sử dụng bất kỳ function hoặc biến nào từ package.

Ví dụ: chúng ta có thể cần đảm bảo rằng function init của package simpleInterest được gọi ngay cả khi chúng ta định không sử dụng package đó ở bất kỳ đâu trong mã của chúng ta. Mã định danh trống _ cũng có thể được sử dụng trong trường hợp này như được hiển thị bên dưới.

package main

import (  
    _ "learnpackage/simpleinterest"
)

func main() {

}

Chạy chương trình trên sẽ có kết quả Simple interest package initialized. Chúng ta đã khởi tạo thành công package simpleinterest mặc dù nó không được sử dụng ở bất kỳ đâu trong mã.

Trong hướng dẫn tiếp theo, chúng ta sẽ tìm hiểu về lệnh điều kiện if else trong Go.

Lệnh điều kiện if else trong Go
Trong hướng dẫn này, chúng ta sẽ xem xét các cú pháp khác nhau và cách sử dụng câu lệnh điều kiện if, if else, if ... else if ... else trong Go.

Hy vọng bạn thích bài viết này. Vui lòng để lại ý kiến ​​và phản hồi có giá trị của bạn :).

Nếu Comdy hữu ích và giúp bạn tiết kiệm thời gian làm việc

Bạn có thể vui lòng đưa Comdy vào whitelist của trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi trong việc trả tiền cho dịch vụ lưu trữ web để duy trì hoạt động của trang web.

Go
Bài Viết Liên Quan:
Phương thức (method) trong Go
Trung Nguyen 02/12/2021
Phương thức (method) trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu phương thức (method) trong Go là gì? Cú pháp khai báo phương thức, so sánh phương thức với hàm, ... trong Go.

Struct trong Go
Trung Nguyen 28/11/2021
Struct trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu struct là gì, cách khai báo và sử dụng một struct trong Go, struct ẩn danh, so sanh hai struct, ...

Con trỏ trong Go
Trung Nguyen 28/11/2021
Con trỏ trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách con trỏ (pointer) hoạt động trong Go và nó khác với con trỏ trong các ngôn ngữ khác như C và C++ như thế nào.

Chuỗi trong Go
Trung Nguyen 28/11/2021
Chuỗi trong Go

Chuỗi (string) xứng đáng được đề cập đặc biệt trong Go vì chúng khác biệt trong cách triển khai khi so sánh với các ngôn ngữ khác.