0

Go Zero to Hero - Bài 45: Import Package: Internal, External và Blank Identifier (_)

Việc quản lý các câu lệnh import và các thư viện phụ thuộc (dependencies) trong Go được quy định bằng những nguyên tắc cực kỳ nghiêm ngặt nhằm duy trì tính sạch sẽ và hiệu năng của mã nguồn.


1. Cú pháp Import chuẩn mực: Quy tắc 3 khối

Khi bạn sử dụng các IDE hiện đại như VSCode hay GoLand, tính năng goimports sẽ tự động format lại code của bạn mỗi khi bấm Save. Khối lệnh import chuẩn trong Go luôn được chia làm 3 nhóm rõ rệt, cách nhau bởi một dòng trống:

package main

import (
    // KHỐI 1: STANDARD LIBRARY (Thư viện chuẩn của Go)
    "context"
    "fmt"
    "net/http"

    // KHỐI 2: EXTERNAL PACKAGES (Thư viện bên thứ 3 tải từ GitHub)
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"

    // KHỐI 3: PROJECT-LOCAL PACKAGES (Các package nội bộ trong dự án của bạn)
    // Cú pháp: <tên_module_trong_go.mod>/đường_dẫn_đến_thư_mục
    "afc-system/internal/delivery/http"
    "afc-system/internal/usecase"
)

⚠️ Luật thép: Trình biên dịch của Go cực kỳ mắc bệnh "sạch sẽ". Nếu bạn import một package mà không sử dụng bất kỳ hàm/biến nào của nó trong code, Go sẽ lập tức báo lỗi biên dịch (Compile Error) và từ chối chạy server. Điều này giúp source code luôn nhẹ và sạch rác.


2. Alias Import: Xử lý đụng độ tên Package

Vì tên package thường là từ cuối cùng của đường dẫn (ví dụ github.com/gin-gonic/gin thì tên package là gin), sẽ có lúc bạn gặp cảnh "đụng hàng" khi import 2 thư viện có cùng một tên.

Ví dụ kinh điển là thư viện tạo số ngẫu nhiên: Go có math/rand (sinh số ngẫu nhiên thường) và crypto/rand (sinh số ngẫu nhiên siêu bảo mật). Giải pháp là đặt bí danh (Alias) ngay trước đường dẫn import.

import (
    "fmt"
    mr "math/rand"   // Đặt bí danh là mr (Math Rand)
    cr "crypto/rand" // Đặt bí danh là cr (Crypto Rand)
)

func main() {
    // Gọi thông qua bí danh thay vì tên package gốc
    normalNumber := mr.Intn(100)
    fmt.Println("Số ngẫu nhiên thường:", normalNumber)
    
    // cr.Read(...)
}

3. Blank Identifier (_): Khải mở bí mật của hàm init()

Đến đây, bạn sẽ gặp một mâu thuẫn lớn:

  1. Luật của Go: Import mà không xài thì sẽ bị báo lỗi.
  2. Thực tế dự án: Khi kết nối PostgreSQL, bạn BẮT BUỘC phải import driver của Postgres, nhưng trong toàn bộ file code của bạn lại không hề gọi ra bất kỳ hàm nào của driver đó (bạn chỉ xài hàm của gói database/sql hoặc GORM).

Làm sao để qua mặt trình biên dịch? Đó là lúc Blank Identifier (_) xuất trận. Việc thêm dấu _ trước đường dẫn mang ý nghĩa: "Tôi import package này, tôi sẽ không gọi hàm nào của nó cả, nhưng hãy kích hoạt Hiệu ứng phụ (Side-effect) của nó cho tôi!"

import (
    "database/sql"
    "fmt"
    
    // Import ẩn: Chỉ để kích hoạt hàm init() của driver Postgres
    _ "github.com/lib/pq" 
)

func main() {
    // Code ở đây chỉ xài database/sql, hoàn toàn không gọi gì từ github.com/lib/pq
    db, err := sql.Open("postgres", "postgres://user:pass@localhost/afc_db")
    if err != nil {
        fmt.Println("Lỗi kết nối DB:", err)
        return
    }
    _ = db
}

Hiệu ứng phụ là gì? Chính là hàm init()

Trong Go, ngoài hàm main(), có một hàm đặc biệt tên là init(). Bất kỳ package nào cũng có thể khai báo hàm init(). Khi ứng dụng khởi động, Go sẽ âm thầm duyệt qua tất cả các package được import. Nếu thấy hàm init(), Go sẽ tự động chạy nó một lần duy nhất trước cả khi hàm main() kịp chạy.

Driver Postgres (github.com/lib/pq) có một hàm init() bên trong mã nguồn của nó làm nhiệm vụ đăng ký cái tên "postgres" vào thư viện lõi của Go. Nhờ Blank Identifier _, hàm init() đó được chạy ngầm, và lệnh sql.Open("postgres", ...) của bạn mới có thể hoạt động.


4. go mod tidy: Người quản gia tận tụy

Để tải các thư viện External (Khối 2) về máy, bạn không cần cài đặt bằng file .exe rườm rà. Bạn chỉ cần làm 2 bước:

  1. Gõ đường dẫn import vào code.
  2. Mở Terminal và gõ lệnh thần thánh:
    go mod tidy
    

Lệnh go mod tidy sẽ tự động dò quét toàn bộ source code của bạn:

  • Thấy thư viện nào bạn import mà chưa có trên máy? Nó tự động tải về và ghi version vào file go.mod.
  • Thấy thư viện nào đang có trong go.mod nhưng bạn đã xóa khỏi code? Nó tự động gỡ bỏ để dự án luôn nhẹ nhàng.

Tổng kết

Bạn đã nắm vững cách lưu thông máu (dữ liệu/logic) giữa các module trong hệ thống:

  1. Quy tắc 3 khối: Standard, External, Local. Luôn giữ form chuẩn để code sạch đẹp.
  2. Alias Import: Đổi tên những package đụng hàng (như math/rand và crypto/rand).
  3. Import ẩn _ (Blank Identifier): Tuyệt chiêu qua mặt trình biên dịch để âm thầm thực thi hàm init() khởi tạo tài nguyên ngầm (đặc biệt quan trọng khi nạp Driver kết nối Database).
  4. Luật thép: Import mà không xài (và không dùng _) thì server sẽ không cho phép biên dịch.

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí