این جزوه به بررسی جنبههای پیشرفته برنامهنویسی در Go میپردازد و شامل موضوعات زیر است:
- بستهها و ماژولها
- مدیریت وابستگیها
- پکیجهای استاندارد
- کار با JSON و فرمتهای داده
- فایلها و I/O
- تستنویسی
هر بخش با توضیحات مفهومی، مثالهای عملی، نکات پیشرفته، و بهترین شیوهها همراه است.
بستهها (Packages) در Go واحدهای سازماندهی کد هستند که امکان استفاده مجدد و ماژولاریتی را فراهم میکنند. ماژولها (Modules) ساختار مدرنتری برای مدیریت پروژهها و وابستگیها ارائه میدهند.
یک بسته مجموعهای از فایلهای Go است که در یک دایرکتوری قرار دارند و نام بسته در ابتدای هر فایل مشخص میشود (معمولاً نام دایرکتوری).
- ساختار دایرکتوری:
mkdir mypackage cd mypackage touch mypackage.go - نوشتن کد بسته:
// mypackage/mypackage.go package mypackage import "fmt" // Hello یک تابع عمومی است (با حرف بزرگ شروع میشود) func Hello(name string) string { return fmt.Sprintf("Hello, %s!", name) } // تابع خصوصی (با حرف کوچک شروع میشود) func secret() string { return "This is private" }
- استفاده از بسته:
// main.go package main import ( "example.com/mypackage" "fmt" ) func main() { fmt.Println(mypackage.Hello("Ali")) // خروجی: Hello, Ali! }
- نامگذاری: نام بسته باید کوتاه، معنادار، و مطابق با نام دایرکتوری باشد.
- دسترسی: متغیرها، توابع، و انواع با حرف بزرگ (مثل
Hello) عمومی هستند؛ با حرف کوچک (مثلsecret) خصوصیاند. - ابتدا ماژول:
go mod init example.com/myapp
Go از مستندات درونخطی پشتیبانی میکند که با ابزار godoc قابل مشاهده است.
- نوشتن مستندات:
// mypackage/mypackage.go package mypackage // Hello یک پیام خوشآمدگویی برای نام دادهشده تولید میکند. func Hello(name string) string { return fmt.Sprintf("Hello, %s!", name) }
- مشاهده مستندات:
یا سرور مستندات:
go doc example.com/mypackage
سپس بهgodoc -http=:6060
http://localhost:6060/pkg/example.com/mypackageبروید.
- مستندات را قبل از تعریف تابع یا نوع بنویسید.
- از جملات کامل و توضیحات واضح استفاده کنید.
- برای مثالها از
// Exampleاستفاده کنید:// ExampleHello یک نمونه از استفاده تابع Hello است. func ExampleHello() { fmt.Println(Hello("Ali")) // Output: Hello, Ali! }
- مخزن Git:
- کد را در یک مخزن عمومی (مثل GitHub) قرار دهید:
github.com/username/mypackage.
- کد را در یک مخزن عمومی (مثل GitHub) قرار دهید:
- تگ نسخه:
git tag v1.0.0 git push origin v1.0.0
- ماژول Go:
- فایل
go.modرا تنظیم کنید:module github.com/username/mypackage go 1.20
- فایل
- استفاده توسط دیگران:
go get github.com/username/mypackage@v1.0.0
- نسخهبندی معنایی: از فرمت
vX.Y.Zاستفاده کنید. - ماژولهای چندبخشی: برای بستههای بزرگ، دایرکتوریهایی مثل
internalو زیرماژولها ایجاد کنید. - انتشار مستندات: از
pkg.go.devبرای نمایش خودکار مستندات استفاده کنید.
- عدم تگ نسخه: باعث خطای
no matching versionsمیشود. - نامگذاری ناسازگار بین
go.modو مسیر مخزن.
Go از سیستم ماژولها برای مدیریت وابستگیها استفاده میکند که با go mod کنترل میشود.
go mod init example.com/myappاین دستور فایل go.mod را ایجاد میکند:
module example.com/myapp
go 1.20برای استفاده از یک بسته خارجی:
go get github.com/some/package@v1.2.3این کار وابستگی را به go.mod اضافه میکند:
require github.com/some/package v1.2.3go get -u github.com/some/packagego mod tidyVendoring کپی کدهای وابستگیها را در پروژه ذخیره میکند تا بدون دسترسی به اینترنت قابل ساخت باشد.
go mod vendorاین کار دایرکتوری vendor را ایجاد میکند.
go build -mod=vendor- Vendoring برای پروژههای قدیمی یا محیطهای آفلاین مفید است.
- در Go مدرن کمتر استفاده میشود، زیرا
go modبهینهتر است.
- Go از نسخهبندی معنایی (Semantic Versioning) پشتیبانی میکند:
vX.Y.Z. - عمده (X): تغییرات ناسازگار.
- جزئی (Y): ویژگیهای جدید.
- رفع باگ (Z): اصلاحات.
- مشخص کردن حداقل نسخه:
require github.com/some/package v1.2.0
- استفاده از نسخه خاص:
go get github.com/some/package@v1.2.3
- ماژولهای جایگزین:
replace github.com/some/package => ../local/package
- پروکسی ماژول: از
GOPROXYبرای دانلود سریعتر استفاده کنید:export GOPROXY=https://proxy.golang.org
- عدم بهروزرسانی
go.modباgo mod tidy. - استفاده از نسخههای ناسازگار.
کتابخانه استاندارد Go شامل پکیجهای قدرتمندی است که برای کارهای روزمره استفاده میشوند.
برای فرمتدهی و چاپ استفاده میشود.
- چاپ:
fmt.Println("Hello") // چاپ ساده fmt.Printf("Name: %s, Age: %d\n", "Ali", 30) // فرمتدهی
- خواندن ورودی:
var name string fmt.Scan(&name)
برای عملیات روی رشتهها:
- جستجو:
s := "Hello, World" fmt.Println(strings.Contains(s, "World")) // true
- جایگزینی:
fmt.Println(strings.ReplaceAll(s, "World", "Go")) // Hello, Go
برای کار با زمان و تاریخ:
- زمان فعلی:
now := time.Now() fmt.Println(now) // 2025-05-17 16:53:00 +0000 UTC
- تأخیر:
time.Sleep(2 * time.Second)
- فرمتدهی:
fmt.Println(now.Format("2006-01-02")) // 2025-05-17
برای عملیات ریاضی:
- توابع پایه:
fmt.Println(math.Sqrt(16)) // 4 fmt.Println(math.Pow(2, 3)) // 8
- ثابتها:
fmt.Println(math.Pi) // 3.141592653589793
package main
import (
"fmt"
"math"
"strings"
"time"
)
func main() {
name := "Ali"
fmt.Printf("Hello, %s!\n", strings.ToUpper(name))
t := time.Now()
fmt.Println("Current time:", t.Format("2006-01-02 15:04"))
fmt.Println("Square root of 16:", math.Sqrt(16))
}- کارایی: از
strings.Builderبه جایstrings.Replaceبرای تغییرات مکرر استفاده کنید. - دقت زمان: برای اندازهگیری عملکرد از
time.Sinceاستفاده کنید:start := time.Now() // عملیات fmt.Println(time.Since(start))
- فرمت نادرست زمان (باید از
2006-01-02استفاده شود). - استفاده از
fmt.Scanبدون مدیریت خطا.
Go از فرمتهای داده مانند JSON، YAML، و XML پشتیبانی میکند.
- Marshal (تبدیل struct به JSON):
type Person struct { Name string `json:"name"` Age int `json:"age"` } p := Person{Name: "Ali", Age: 30} data, err := json.Marshal(p) if err != nil { log.Fatal(err) } fmt.Println(string(data)) // {"name":"Ali","age":30}
- Unmarshal (تبدیل JSON به struct):
jsonStr := `{"name":"Ali","age":30}` var p Person err := json.Unmarshal([]byte(jsonStr), &p) if err != nil { log.Fatal(err) } fmt.Println(p) // {Ali 30}
- تگهای
jsonبرای کنترل نام فیلدها و رفتار (مثلomitempty) استفاده میشوند:type Person struct { Name string `json:"name,omitempty"` Age int `json:"age"` }
- برای JSONهای پویا از
map[string]interface{}یا[]interface{}استفاده کنید.
برای YAML نیاز به بسته خارجی (مثل gopkg.in/yaml.v3) دارید:
go get gopkg.in/yaml.v3- مثال:
type Config struct { Name string `yaml:"name"` Age int `yaml:"age"` } yamlStr := "name: Ali\nage: 30" var c Config err := yaml.Unmarshal([]byte(yamlStr), &c) if err != nil { log.Fatal(err) } fmt.Println(c) // {Ali 30}
مشابه JSON، با پکیج encoding/xml:
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
}
xmlStr := `<person><name>Ali</name><age>30</age></person>`
var p Person
err := xml.Unmarshal([]byte(xmlStr), &p)
fmt.Println(p) // {Ali 30}- Streaming: برای فایلهای JSON/XML بزرگ، از
json.Decoderوxml.Decoderاستفاده کنید:dec := json.NewDecoder(strings.NewReader(jsonStr)) var p Person dec.Decode(&p)
- مدیریت خطا: همیشه خطاهای Marshal/Unmarshal را بررسی کنید.
- عدم تطابق تگها با ساختار داده.
- استفاده از اشارهگر نادرست در Unmarshal.
Go ابزارهای قدرتمندی برای کار با فایلها و عملیات ورودی/خروجی ارائه میدهد.
data := []byte("Hello, Go!")
err := os.WriteFile("output.txt", data, 0644)
if err != nil {
log.Fatal(err)
}data, err := os.ReadFile("output.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data)) // Hello, Go!- ایجاد:
err := os.Mkdir("mydir", 0755)
- لیست کردن:
entries, err := os.ReadDir("mydir") for _, entry := range entries { fmt.Println(entry.Name()) }
برای بهبود کارایی از bufio استفاده کنید:
- خواندن:
file, err := os.Open("input.txt") if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { fmt.Println(scanner.Text()) }
- نوشتن:
file, err := os.Create("output.txt") writer := bufio.NewWriter(file) writer.WriteString("Hello, Go!") writer.Flush() // اطمینان از نوشتن
- مدیریت منابع: همیشه از
defer file.Close()استفاده کنید. - فایلهای بزرگ: برای خواندن/نوشتن تدریجی از
io.Readerوio.Writerاستفاده کنید. - همزمانی: برای عملیات I/O سنگین، از goroutineها استفاده کنید.
- عدم بستن فایلها.
- نادیده گرفتن خطاهای I/O.
Go ابزارهای داخلی قدرتمندی برای تستنویسی ارائه میدهد.
- تست ساده:
// math.go package math func Add(a, b int) int { return a + b } // math_test.go package math import "testing" func TestAdd(t *testing.T) { result := Add(2, 3) if result != 5 { t.Errorf("Add(2, 3) = %d; want 5", result) } }
- اجرا:
go test
برای تست چندین سناریو:
func TestAdd(t *testing.T) {
tests := []struct {
a, b, want int
}{
{2, 3, 5},
{0, 0, 0},
{-1, 1, 0},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
if got := Add(tt.a, tt.b); got != tt.want {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
})
}
}برای اندازهگیری عملکرد:
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}اجرا:
go test -bench=.- Mocking: از بستههای مثل
testifyبرای mock استفاده کنید:go get github.com/stretchr/testify
- Coverage:
go test -cover - تستهای همزمانی: از
sync.WaitGroupبرای هماهنگی استفاده کنید.
- نادیده گرفتن خطاها در تستها.
- نوشتن تستهای غیرقابل نگهداری.
- سازماندهی کد: بستهها را بر اساس مسئولیت تقسیم کنید (مثل
api,db,utils). - مدیریت وابستگیها: همیشه
go mod tidyرا اجرا کنید. - استفاده بهینه از کتابخانه استاندارد: به جای بستههای خارجی، ابتدا پکیجهای استاندارد را بررسی کنید.
- تستنویسی منظم: برای هر تغییر، تست جدید بنویسید.
- مستندسازی: مستندات را بهروز نگه دارید و از مثالهای قابل اجرا استفاده کنید.
این جزوه تمام جنبههای برنامهنویسی پیشرفته در Go را با جزئیات کامل پوشش داد. از ایجاد بستهها و مدیریت وابستگیها تا کار با فرمتهای داده، فایلها، و تستنویسی، هر بخش با مثالهای عملی و نکات پیشرفته ارائه شد. برای یادگیری عمیقتر:
- مستندات رسمی:
https://golang.org/doc/ - کتاب "The Go Programming Language"
- تمرین با پروژههای واقعی مثل ساخت API یا ابزار CLI