Hàm `init`
Thuộc phần Logic và luồng điều khiển trong hành trình GO của Coddy. Bài học 56 trên 68.
Hàm init là một hàm đặc biệt trong Go, tự động chạy khi một package được nhập. Khác với các hàm thông thường, bạn không gọi trực tiếp init, Go sẽ chạy hàm này cho bạn trong quá trình khởi tạo package.
Sau đây là cách bạn định nghĩa một hàm init:
package mypackage
import "fmt"
func init() {
fmt.Println("Package mypackage is being initialized!")
}Hàm init có một số đặc điểm riêng: không nhận tham số, không trả về giá trị nào và chạy trước khi hàm main bắt đầu. Vì vậy, hàm này rất phù hợp để thiết lập trạng thái ban đầu, đăng ký các thành phần hoặc thực hiện những tác vụ thiết lập một lần mà package của bạn cần.
Khi bạn import một package có hàm init, bạn sẽ thấy tác động của nó ngay lập tức, ngay cả khi không dùng bất kỳ hàm nào được package xuất ra. Đây là lý do cách import bằng định danh trống mà bạn đã học trước đó rất hữu ích: nó cho phép bạn kích hoạt quá trình khởi tạo của package mà không trực tiếp dùng code của package.
Thử thách
DễTạo một hệ thống khởi tạo package để minh họa cách các hàm init tự động chạy khi package được nhập. Bạn sẽ xây dựng một hệ thống ghi nhật ký, trong đó các package khác nhau tự khởi tạo và ghi lại thứ tự khởi động.
Bạn sẽ nhận được hai đầu vào:
- Một chuỗi chứa các định nghĩa package theo định dạng
"package1:message1,package2:message2,package3:message3"(ví dụ:"database:Database connection pool initialized,cache:Cache system ready,logger:Logging system started") - Một chuỗi chứa các thao tác của chương trình chính theo định dạng
"operation1,operation2,operation3"(ví dụ:"start_server,process_requests,shutdown")
Nhiệm vụ của bạn:
- Phân tích đầu vào thứ nhất bằng cách tách chuỗi tại các dấu phẩy để lấy từng định nghĩa package
- Với mỗi định nghĩa package, tách chuỗi tại các dấu hai chấm để lấy tên package và thông báo khởi tạo
- Tạo một lát cắt toàn cục có tên
initLogđể ghi lại các thông báo khởi tạo - Tạo một hàm có tên
logInitnhận tên package và thông báo, sau đó thêm thông báo đã định dạng"[INIT] [package_name]: [message]"vào lát cắtinitLog - Mô phỏng quá trình khởi tạo package bằng cách gọi
logInitcho từng package theo thứ tự xuất hiện trong đầu vào - Hiển thị tiêu đề thứ tự khởi tạo:
"=== PACKAGE INITIALIZATION SEQUENCE ===" - In từng thông báo khởi tạo từ lát cắt
initLog - Hiển thị tiêu đề chương trình chính:
"=== MAIN PROGRAM EXECUTION ===" - In:
"All packages initialized successfully" - Phân tích đầu vào thứ hai bằng cách tách chuỗi tại các dấu phẩy để lấy từng thao tác
- Với mỗi thao tác trong chương trình chính:
- In:
"Executing: [operation]" - Thêm thông báo vào nhật ký:
"[MAIN] Operation: [operation]"
- In:
- Hiển thị phần tóm tắt quá trình thực thi:
"=== EXECUTION SUMMARY ===""Packages initialized: [number_of_packages]""Main operations executed: [number_of_operations]""Total log entries: [total_number_of_log_entries]"
- Hiển thị nhật ký đầy đủ:
"=== COMPLETE EXECUTION LOG ==="- In từng mục trong nhật ký đầy đủ, gồm cả thông báo khởi tạo và các thao tác của chương trình chính
- Hiển thị thông báo cuối cùng:
"Program execution completed - all init functions ran before main"
Dùng package strings để tách các chuỗi đầu vào và package fmt để định dạng đầu ra. Thử thách này minh họa cách các hàm init tự động chạy trong quá trình nhập package và luôn chạy trước hàm main, nhờ đó chúng rất phù hợp cho các tác vụ thiết lập và khởi tạo package.
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// Lát cắt toàn cục để theo dõi các thông báo khởi tạo
var initLog []string
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc đầu vào (bufio xử lý đúng các dòng có dấu cách)
packageDefs, _ := reader.ReadString('\n')
packageDefs = strings.TrimRight(packageDefs, "\r\n")
mainOps, _ := reader.ReadString('\n')
mainOps = strings.TrimRight(mainOps, "\r\n")
// TODO: Viết code của bạn bên dưới
// 1. Tách packageDefs theo "," để lấy từng mục package
// 2. Với mỗi mục, tách theo ":" (tối đa 2 phần) để lấy packageName và message
// 3. Tạo hàm (function) logInit(packageName, message string) để thêm
// "[INIT] [packageName]: message" vào lát cắt toàn cục initLog
// 4. Hiển thị tiêu đề trình tự khởi tạo và tất cả mục trong nhật ký khởi tạo
// 5. Hiển thị tiêu đề thực thi chính và xử lý từng thao tác từ mainOps
// - In "Executing: <op>" cho mỗi thao tác
// - Thêm "[MAIN] Operation: <op>" vào initLog
// 6. Hiển thị phần tổng kết thực thi (số package, số thao tác, tổng số mục nhật ký)
// 7. Hiển thị toàn bộ nhật ký và thông báo hoàn tất cuối cùng
}Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Logic và luồng điều khiển
1Luồng điều khiển nâng cao
Switch với `fallthrough`Thoát khỏi vòng lặp lồng nhauTiếp tục một vòng lặp cụ thểCâu lệnh `goto`Ôn tập: điều khiển vòng lặp nâng cao4Dự án: Danh sách việc cần làm đơn giản
Thiết lập dự ánThêm một việc cần làm2Struct và phương thức
Định nghĩa phương thức cho structReceiver giá trịReceiver con trỏChọn receiverPhương thức và hàmÔn tập: hành vi của struct5Tìm hiểu chuyên sâu về map
Map chứa structCon trỏ làm giá trị của mapKiểm tra map nilSo sánh các mapÔn tập: bộ đếm tần suất từ8Dự án: Quản lý hàng tồn kho đơn giản
Tổng quan dự ánKiểm tra hàng tồn kho3Interface (cơ bản)
Interface là gì?Định nghĩa interfaceTriển khai interfaceDùng kiểu interfaceInterface rỗngKhẳng định kiểuType switchÔn tập: hình dạng và hành viTự luyện tập: Trình biên dịch Go online