با توابع init در گولنگ بیشتر آشنا شویم

با توابع init در گولنگ بیشتر آشنا شویم

در زبان برنامه‌نویسی گولنگ، هر برنامه با فراخوانی تابع main از پکیج main شروع می‌شه و با بازگشت از این تابع، برنامه به پایان میرسه. توابع init نیز نقش مهمی دارند که توی این مقاله قصد دارم کمی بیشتر در مورد این توابع، ویژگی ها و نحوه استفاده از توابع init صحبت کنم.

توی زبان برنامه‌نویسی گولنگ، توابع init برای انجام تنظیمات اولیه قبل از اجرای برنامه استفاده میشن. هر فایل Go میتونه یک یا چند تابع init داشته باشه که به طور خودکار و بدون نیاز به فراخوانی مستقیم اجرا میشن. این توابع بیشتر برای آماده‌سازی محیط، مانند تنظیمات پکیج‌ها، مقداردهی اولیه متغیرهای بسته و یا انجام عملیات‌هایی که باید قبل از شروع برنامه انجام شوند، به کار می‌روند.

برخی از ویژگی‌های مهم توابع init:

  • ترتیب اجرای توابع init بر اساس ترتیب فایل‌های موجود در پکیج هست.
  • توابع init در یک پکیج، قبل از توابع main اجرا می‌شن.
  • این توابع نمیتونن به طور مستقیم فراخوانی بشن و باید بدون ورودی و خروجی باشن.

 

توابع init به اصطلاح در package block (محدوده‌ای که در آن پکیج و اجزای مربوط به آن تعریف می‌شوند) بصورت زیر تعریف میشه:

فرض کنید یه پکیج بنام main داریم، روش تعریف یک تابع init بصورت زیر هست:
 

package main

import "fmt"

var x int

func init() {
    x = 10
}

func main() {
    fmt.Println(x) // خروجی: 10
}

 

همونطور که در کد بالا میبینید ما یک متغییرx در تابع init تعریف کرده و از آن در تابع main استفاده کردیم. بطور کلی توابع init در محدوده ی بلاک تعریف و قبل از اجرای تابع main برای تنظیمات و مقداردهی اولیه استفاده میشن.

 

بصورت کلی از توابع  init در گولنگ برای کارهای زیر استفاده می شه:

مقداردهی اولیه متغیرها اگر در عبارت مقداردهی اولیه قابل انجام نباشد،
بررسی/تثبیت وضعیت برنامه،
ثبت نام (registering)،
اجرای محاسباتی که قرار هست فقط یکبار انجام شوند،
و...

همچنین هر چیزی که می تونید توی یک تابع معمولی قرار بدین را میتونید توی init قرار بدین.

 

در ادامه به برخی نکات مهم در مورد توابع init در گولنگ می پردازیم:

 

مقداردهی اولیه پکیج ها:

برای استفاده از یک یکیچ ایمپورت شده(imported)، ابتدا باید مقداردهی اولیه صورت گیرد. این کار توسط سیستم در زمان اجرای Go انجام میشه و شامل مراحل زیر هست ( در اینجا ترتیب اهمیت داره):

  1. مقداردهی اولیه بسته‌های وارد شده (به صورت بازگشتی).
  2. محاسبه و تخصیص مقادیر اولیه برای متغیرهای تعریف شده در بلوک بسته.
  3. اجرای توابع init در داخل بسته.

همچنین باید بدونیم که مقداردهی اولیه بسته تنها یک بار انجام می‌شود، حتی اگر بسته چندین بار وارد شود.

 

ترتیب اجرا:

در زبان گولنگ یه بسته می تونه حاوی فایل های زیادی باشه. اما ترتیب مقدار دهی اولیه متغیرها و فراخوانی توابع init وقتی که چندین init در فایل های متفاوت داریم به چه صورته؟  ابتدا، مکانیزم وابستگی مقداردهی اولیه وارد عمل می‌شه. بعد باید تصمیم‌گیری بشه که آیا مقداردهی متغیرها در فایل a.go باید زودتر انجام شود یا مثلاً در z.go.

این موضوع به ترتیب فایل‌ها که به کامپایلر ارائه می‌شود بستگی دارد. اگر فایل z.go ابتدا توسط سیستم بیلد ارسال بشه، مقداردهی متغیرها در آنجا زودتر از a.go انجام می‌شود. همین مورد برای اجرای توابع init نیز صدق می‌کند. مشخصات زبان توصیه می‌کند که همیشه از همان ترتیب استفاده شود و نام فایل‌های بسته را به ترتیب لغوی به کامپایلر ارسال کنید:

"برای اطمینان از رفتار قابل بازتولید مقداردهی اولیه، سیستم‌های بیلد تشویق می‌شوند که فایل‌های متعدد متعلق به یک بسته را به ترتیب نام فایل‌های لغوی به یک کامپایلر ارائه دهند."

- ترتیب لغوی به معنی ترتیب حروف الفبایی نام فایل‌ها هست. به عنوان مثال، اگر نام فایل‌های شما a.go, b.go, c.go باشد، باید به ترتیب a.go، سپس b.go، و در نهایت c.go به کامپایلر ارسال شوند.

هدف از این توصیه این است که فرآیند مقداردهی اولیه (initialization) در برنامه‌های Go قابل پیش‌بینی و تکرارپذیر باشد. اگر همیشه فایل‌ها به یک ترتیب مشخص و ثابت (مثلاً حروف الفبایی) به کامپایلر ارسال بشن، نتیجه‌ی مقداردهی اولیه متغیرها و اجرای توابع init نیز همیشه یکسان خواهد بود، که این موضوع به ایجاد برنامه‌هایی پایدارتر و کمتر متغیر کمک می‌کند.

البته این روش ممکنه باعث ایجاد برنامه های کمتر قابل حمل شود یعنی دو محیط های متفاوت رفتار متفاوتی نشون خواهد داد.

مثال زیر رو ببیند:

// a.go
package main

import "fmt"

var a = c + b // 3 + 2 = 5

func init() {
    fmt.Println("init a")
}

func main() {
    fmt.Println("main")
}

// b.go
package main

var b = 2

func init() {
    fmt.Println("init b")
}

// c.go
package main

var c = 3

func init() {
    fmt.Println("init c")
}

 

توی کد بالا، ترتیب اجرای توابع init و مقداردهی اولیه متغیرها به ترتیب فایل‌هایی که به کامپایلر ارسال شده‌اند بستگی داره.

 اگر سیستم بیلد فایل‌ها را به ترتیب a.go, b.go, c.go ارسال کنه، خروجی به این صورت خواهد بود:

init a
init b
init c
main

اما اگر ترتیب فایل‌ها به صورت c.go, b.go, a.go باشد، خروجی متفاوت خواهد بود:

init c
init b
init a
main

 

 

این تفاوت در خروجی نشان میده که وابستگی به ترتیب فایل‌ها میتونه مشکلاتی در قابل حمل بودن برنامه ایجاد کنه. به همین دلیل توصیه می شه که از ترتیب لغوی(حروف الفبایی) برای ارسال فایل ها به کامپایلر استفاده بشه تا رفتار برنامه در همه محیط ها یکسان و قابل پیشبینی باشه.


ویژگی های توابع init:

عدم تعریف به صورت صریح:
این توابع  نه آرگومان می گیرن و نه مقداری برمی گردونن. بر خلاف main، تابع init اعلان نشده است، بنابراین نمی توان به اون ارجاع داد، برای نمونه کد زیر یک خطا صادر میکنه:

package main
import "fmt"
func init() {
    fmt.Println("init")
}
func main() {
    init()
}

کد بالا رو اگه ران کنید با خطای undefined: init مواجه میشین. این خطا بخاطر این هست که شناسگر init به عنوان یک تابع مستقل در برنامه تعریف نشده.

به عبارت دیگه، تابع init نمیتونه به صورت مستقیم مانند سایر توابع مورد استفاده قرار بگیره. شبیه به شناسگر خالی (blank identifier) هست که با کاراکتر آندرلاین (_) نشان داده میشه.

بنابراین، تابع init در Go یک تابع خاص هست که فقط برای مقداردهی اولیه و تنظیمات خاص قبل از شروع برنامه استفاده میشه و نمی‌توان به آن مانند سایر توابع ارجاع داد یا اون رو فراخوانی کرد.

 

تعریف چند تابع init در یک فایل:

در زبان گولنگ میتونیم توی یک فایل چندین تابع init داشته باشیم، کد زیر رو ببینید:

فایل sandbox.go:

package main

import "fmt"

func init() {
    fmt.Println("init 1")
}

func init() {
    fmt.Println("init 2")
}

func main() {
    fmt.Println("main")
}

 

فایل utils.go:

package main

import "fmt"

func init() {
    fmt.Println("init 3")
}

 

خروجی دو فایل بالا بصورت زیر خواهد بود:

init 1
init 2
init 3
main

 

همونطور که میبینید در زمان اجرای برنامه، توابع init به ترتیبی که در فایل‌ها تعریف شدن اجرا میشن، و بعد از اونها تابع main اجرا می‌شه.

 

بطور کلی  توابع init در Go برای انجام مقداردهی‌های پیچیده و تنظیمات اولیه بسیار مفید هست و میتونه به صورت انعطاف‌پذیری در بسته‌ها و فایل‌های مختلف تعریف بشه.

 

امیدوارم این آموزش برای شما مفید بوده باشه. اگر نظر یا پیشنهادی دارید از بخش نظرات برای ما ارسال کنید.


دسته بندی ها:

گولنگ

ارسال نظر

برای اطلاع از پاسخ به نظر شما می توانید ایمیل یا شماره موبایل خود را وارد نمایید. *

ایمیل و شماره موبایل شما کاملا مخفی خواهد ماند و در سایت نمایش داده نخواهد شد. *

اگر نظری برای این مطلب ارسال شد از طریق ایمیل مرا اطلاع بده!
لسیت نظرات
هنوز برای این مطلب نظری ارسال نشده است!