۶.۶.۱ استفاده از interface{} و reflect قبل از ژنریکها #
پیش از Go 1.18، برای پیادهسازی توابع یا ساختارهای داده عمومی، معمولاً از نوع interface{} (نوع همهکاره) استفاده میشد. در موارد نیاز به عملیات خاص یا تبدیل نوع، ناچار به استفاده از reflect یا type assertion بودیم. این روشها معایب و ریسکهای خاص خود را داشتند.
مثال: تابع Max با interface{} و reflect #
package main
import (
	"fmt"
	"reflect"
)
func Max(a, b interface{}) interface{} {
	av := reflect.ValueOf(a)
	bv := reflect.ValueOf(b)
	if av.Kind() == reflect.Int && bv.Kind() == reflect.Int {
		if av.Int() > bv.Int() {
			return a
		}
		return b
	}
	// میتوانید برای انواع دیگر هم کد بنویسید
	return nil
}
func main() {
	fmt.Println(Max(3, 7)) // خروجی: 7
}
توضیح: در این مثال تابع Max با هر نوعی که به آن بدهید کار میکند، اما باید به کمک reflect نوع مقدار را بررسی و مقایسه کنید. این کار هم کند است و هم ایمنی نوعی ندارد و در زمان اجرا ممکن است باعث panic یا رفتار ناخواسته شود.
۶.۶.۲ مزایا و معایب هر روش #
interface{} و reflect (روش قدیمی) #
مزایا:
- قابلیت انعطاف برای پذیرش هر نوع داده (generic ظاهر)
 - قابل استفاده در زبانهای قبل از Go 1.18
 
معایب:
- کاهش ایمنی نوعی (Type Safety): خطاهای نوع فقط در زمان اجرا کشف میشوند.
 - پیچیدگی و خوانایی پایین: بررسی نوع با reflect یا type assertion باعث طولانی و پیچیده شدن کد میشود.
 - افت کارایی: بازتاب (reflect) کند است و فراخوانیهای زیاد باعث overhead میشود.
 - خطر panic: اگر نوع داده اشتباه ارسال شود، احتمال panic بالا میرود.
 - عدم هشدار کامپایلری: هیچ هشدار یا خطایی از سمت کامپایلر دریافت نمیکنید.
 - تست و نگهداری دشوار: تست و اشکالزدایی کدهایی که مبتنی بر interface{} و reflect هستند به مراتب سختتر است.
 
ژِنریکهای Go (از 1.18 به بعد) #
مزایا:
- ایمنی نوعی بالا: همه خطاهای نوع در زمان کامپایل مشخص میشوند.
 - کد کوتاهتر و خواناتر: نیاز به تکرار تابع برای هر نوع داده نیست و بازتاب حذف میشود.
 - کارایی بهتر: هیچ overhead ناشی از reflect یا type assertion وجود ندارد و کد تولیدشده شبیه کد دستی است.
 - نگهداری آسانتر: refactoring راحتتر و تستپذیری بالاتر
 - مستندسازی خودکار و بهتر: امضاهای توابع و structها واضح و قابل فهم برای توسعهدهندگان و ابزارهاست.
 
معایب:
- نیاز به نسخه جدید Go: فقط در Go 1.18 به بعد قابل استفاده است.
 - درک اولیه برای توسعهدهندگان تازهکار ممکن است کمی زمانبر باشد.
 - در برخی موارد خاص (مانند ژنریک اینترفیسهای خیلی پیچیده)، امضاها میتواند کمی پیچیده شود.
 
۶.۶.۳ مقایسه کارایی، خوانایی و نگهداشت #
| ویژگی | interface{} و reflect (قدیمی) | ژنریکهای Go (جدید) | 
|---|---|---|
| ایمنی نوع | بسیار پایین (خطر panic بالا) | بسیار بالا (compile-time checked) | 
| خوانایی | پایین و پیچیده (reflect و assert) | بالا و شفاف (type-safe) | 
| کارایی | کند (overhead بازتاب) | سریع (مانند کد معمولی) | 
| نگهداری | دشوار و پرخطا | آسان و قابل refactor | 
| تستپذیری | سخت (خطاهای run-time) | بسیار آسان (خطاهای compile-time) | 
| کد تکراری | زیاد (اگر برای هر نوع دستی بنویسید) | حداقل (یک بار برای همه انواع) |