
اگر برای اولین بار کدهای گو را ببینید، احتمالا اولین واکنش شما چیزی شبیه این خواهد بود:
«همین؟!»
نه خبری از کلاسهای پیچیده است، نه از ارثبری چندلایه، نه از دهها نوع تعریف مختلف برای ساختارهای داده و نه از ویژگیهایی که برای فهمیدن آنها باید چندین کتاب بخوانید.
اما نکته جالب اینجاست که همین زبان ظاهرا ساده، زیرساخت برخی از بزرگترین پروژههای نرمافزاری جهان را تشکیل میدهد. ابزارهایی مانند داکر، کوبرنتیز، ترافورم، پرومتئوس و بسیاری از سرویسهای ابری مدرن با گو توسعه داده شدهاند.
این سوال مطرح میشود:
اگر گولنگ اینقدر ساده است، چرا شرکتهای بزرگی مانند گوگل، اوبر، کلادفلر و دراپ باکس از آن استفاده میکنند؟
پاسخ در فلسفه طراحی این زبان نهفته است. گولنگ تلاش نمیکند همه چیز را به توسعهدهنده بدهد، بلکه فقط ابزارهایی را ارائه میکند که واقعا برای ساخت نرمافزارهای مقیاسپذیر نیاز هستند.
در این مقاله بررسی میکنیم که چرا سینتکس گو تا این حد ساده است و چگونه همین سادگی باعث ایجاد قدرت بیشتر در پروژههای واقعی میشود.
زمانی که مهندسان گوگل شروع به طراحی گو کردند، با یک مشکل اساسی مواجه بودند.
کدبیسهای بزرگ سازمانی روزبهروز پیچیدهتر میشدند.
در بسیاری از زبانها:
زمان کامپایل طولانی بود
نگهداری پروژه دشوار بود
تعداد زیادی ویژگی زبان وجود داشت
خواندن کد دیگران زمان زیادی میگرفت
هدف گو این بود که:
برنامهنویسان بتوانند کد یکدیگر را سریعتر بخوانند تا اینکه صرفا بتوانند ویژگیهای عجیب و غریب زبان را استفاده کنند.
به همین دلیل تیم سازنده تصمیم گرفت بسیاری از قابلیتهایی که در سایر زبانها محبوب بودند را حذف کند.
برای مثال:
ارثبری کلاسیک حذف شد
Operator Overloading حذف شد
Exception حذف شد
Generics سالها عمدا اضافه نشد
متاپروگرمینگ پیچیده حذف شد
نتیجه این تصمیمات زبانی شد که یادگیری آن بسیار سریعتر از اکثر زبانهای مدرن است.
یکی از دلایل سادگی گو تعداد بسیار کم کلمات کلیدی آن است.
در حالی که برخی زبانها دهها یا حتی صدها کلمه رزروشده دارند، گولنگ تنها تعداد محدودی کیورد دارد.
نمونههایی از آن:
func package import var const if for switch go defer
این موضوع چند مزیت مهم دارد:
یادگیری سریعتر
خوانایی بیشتر
کاهش خطاهای نحوی
یکپارچگی کدها
به همین دلیل بسیاری از برنامهنویسان تنها پس از چند روز میتوانند تقریباً تمام سینتکس گو را یاد بگیرند.
در بسیاری از زبانها انواع مختلفی از حلقهها وجود دارد:
for while do while foreach
اما در گو تقریبا همه چیز با یک دستور انجام میشود:
for i := 0; i < 10; i++ { fmt.Println(i) }
حتی حالت while نیز با همین ساختار پیادهسازی میشود:
for condition { // code }
این تصمیم باعث شده زبان کوچکتر و قابل پیشبینیتر شود.
برنامهنویس لازم نیست تصمیم بگیرد از کدام نوع حلقه استفاده کند.
در گو بسیاری از نشانههای غیرضروری حذف شدهاند.
برای مثال:
if age > 18 { fmt.Println("Adult") }
در حالی که در برخی زبانها (مثلا PHP) باید بنویسید:
if ($age > 18) { echo "Adult"; }
حذف پرانتزهای اضافی باعث شده کدها تمیزتر و خواناتر باشند.
شاید در نگاه اول این تفاوت کوچک به نظر برسد اما در پروژههای چندصدهزار خطی تاثیر قابل توجهی دارد.
یکی از بزرگترین دلایل موفقیت گو ابزاری به نام gofmt است.
این ابزار به صورت خودکار کدها را فرمت میکند.
مثلا اگر کد شما به شکل زیر باشد:
if x>10{ fmt.Println(x) }
ابزار gofmt آن را به صورت استاندارد تبدیل میکند:
if x > 10 { fmt.Println(x) }
نتیجه چیست؟
هیچ جنگی بر سر Style وجود ندارد.
در بسیاری از تیمها ساعتها زمان صرف بحث درباره موارد زیر میشود:
فاصلهها
Tab یا Space
محل آکولادها
نحوه چینش خطوط
در گو این اختلافات تقریبا از بین میروند.
در گو تعریف متغیرها بسیار سرراست است.
var name string
یا حتی کوتاهتر:
name := "Mojtaba"
کامپایلر نوع داده را تشخیص میدهد.
این ویژگی باعث میشود کدها کوتاهتر شوند اما همچنان خوانایی خود را حفظ کنند.
یکی از جنجالیترین تصمیمات گو حذف Inheritance یا ارث بری بود.
در بسیاری از زبانهای شیگرا شاهد ساختارهایی مانند این هستیم:
Animal └── Mammal └── Dog └── Husky
هرچه پروژه بزرگتر میشود این سلسلهمراتب پیچیدهتر خواهد شد.
گو راه متفاوتی را انتخاب کرد.
به جای ارثبری از Composition استفاده میکند.
type Engine struct { Power int } type Car struct { Engine }
این رویکرد چند مزیت مهم دارد:
وابستگی کمتر
تستپذیری بهتر
انعطاف بیشتر
فهم آسانتر ساختار پروژه
یکی از قدرتمندترین قابلیتهای گولنگ اینترفیسها هستند.
نمونه:
type Speaker interface { Speak() }
هر ساختاری که متد Speak را داشته باشد به صورت خودکار این Interface را پیادهسازی میکند.
type Dog struct{} func (d Dog) Speak() { fmt.Println("Woof") }
نیازی به:
implements Speaker
وجود ندارد.
این موضوع Coupling را به شدت کاهش میدهد.
در بسیاری از زبانها مدیریت خطا به شکل زیر انجام میشود:
try { ... } catch { ... }
اما گو از رویکرد دیگری استفاده میکند:
result, err := doSomething() if err != nil { return err }
در نگاه اول ممکن است این روش تکراری به نظر برسد.
اما مزایای مهمی دارد:
رفتار برنامه کاملا قابل پیشبینی است
خطاها پنهان نمیشوند
دیباگ کردن آسانتر میشود
به همین دلیل بسیاری از مهندسان سیستمهای بزرگ این رویکرد را ترجیح میدهند.
بسیاری از زبانها برای پردازش همزمان نیازمند کتابخانههای جانبی هستند.
اما در گو این قابلیت از ابتدا در هسته زبان قرار گرفته است.
نمونه:
go sendEmail()
فقط با اضافه کردن کلمه کلیدی گو یک Goroutine ساخته میشود.
این سادگی خارقالعاده است.
پیادهسازی Thread در بسیاری از زبانها به مراتب پیچیدهتر است.
گولنگ تنها اجرای همزمان را ساده نکرده است.
بلکه ارتباط بین پردازشها را نیز آسان کرده است.
messages := make(chan string) go func() { messages <- "hello" }() msg := <-messages
این ساختار باعث میشود:
Race Condition کمتر شود
همزمانی ایمنتر شود
کدها خواناتر باشند
یکی از اهداف اصلی گو سرعت بالای Build بود.
در پروژههای بزرگ:
Java
C++
Scala
ممکن است فرآیند Build زمانبر شود.
اما گو به شکلی طراحی شده که کامپایل آن بسیار سریع باشد.
این موضوع مستقیما روی بهرهوری تیم توسعه اثر میگذارد.
یکی از قوانین نانوشته گو این است:
کدی بنویس که دیگران سریع بفهمند، نه کدی که فقط تو بتوانی بنویسی.
به همین دلیل در گو کمتر شاهد کدهای عجیب و پیچیده هستیم.
برای مثال توسعهدهندگان گو معمولا از:
متدهای کوتاه
ساختارهای ساده
وابستگیهای محدود
استفاده میکنند.
این فرهنگ در جامعه گو نیز بسیار پررنگ است.
فرض کنید یک توسعهدهنده جدید وارد تیم شود.
اگر پروژه با زبانی بسیار پیچیده نوشته شده باشد:
زمان آموزش افزایش پیدا میکند
احتمال خطا بیشتر میشود
هزینه استخدام بالاتر میرود
اما در Go اکثر توسعهدهندگان ظرف چند هفته میتوانند کدبیس را درک کنند.
این مزیت در شرکتهای بزرگ اهمیت فوقالعادهای دارد.
برخی افراد تصور میکنند:
اگر زبان ساده باشد پس ضعیف است.
اما گو خلاف این موضوع را ثابت کرده است.
با همین سینتکس ساده میتوان:
سیستمهای توزیعشده
APIهای بزرگ
زیرساخت ابری
ابزارهای دوآپس
سرویسهای Real-Time
سیستمهای پردازش میلیونها درخواست
را پیادهسازی کرد.
قدرت واقعی گو در حذف قابلیتهای غیرضروری است.
مزایا:
سینتکس سادهتر
کامپایل سریعتر
مصرف حافظه کمتر
کانکارنسی بهتر
مزایا:
سرعت اجرا بسیار بالاتر
تایپ ایمنتر
مناسبتر برای بکاندهای سنگین
مزایا:
استفاده بهتر از چند هسته CPU
مصرف RAM کمتر
پایداری بیشتر در بارهای سنگین
مزایا:
کانکارنسی داخلی
Binary مستقل
عملکرد بهتر در سرویسهای Real-Time
بله.
برخی توسعهدهندگان از نبود برخی قابلیتها ناراضی هستند:
ارثبری کلاسیک
Metaprogramming
Reflection گسترده
Exception Handling
اما همین محدودیتها بخشی از فلسفه گو هستند.
هدف گو این نیست که هر مسئلهای را به پیچیدهترین شکل ممکن حل کند.
هدف آن ساخت نرمافزارهای قابل نگهداری است.
در سالهای اخیر گو رشد قابل توجهی داشته است.
افزوده شدن Generics، بهبود Garbage Collector و توسعه اکوسیستم باعث شده این زبان بیش از گذشته مورد توجه قرار گیرد.
امروزه گو یکی از محبوبترین گزینهها برای:
توسعه بکاند
پردازش ابری
اکوسیستم کوبرنتیز
ابزار دوآپس
سیستمهای توزیع شده
APIهای با پرفورمنس بالا
محسوب میشود.
سادگی سینتکس گولنگ یک اتفاق تصادفی نیست، بلکه نتیجه سالها تجربه مهندسانی است که با مشکلات واقعی نرمافزارهای بزرگ دستوپنجه نرم کردهاند.
گو تلاش نکرده همه قابلیتهای ممکن را در خود جای دهد. در عوض، روی ویژگیهایی تمرکز کرده که بیشترین تاثیر را بر توسعه نرمافزارهای واقعی دارند؛ ویژگیهایی مانند خوانایی بالا، کامپایل سریع، مدیریت همزمانی قدرتمند و نگهداری آسان.
همین رویکرد باعث شده است که گولنگ در عین سادگی، توانایی اجرای پروژههایی را داشته باشد که روزانه میلیونها یا حتی میلیاردها درخواست را پردازش میکنند.
اگر هنوز گو را امتحان نکردهاید، احتمالا بزرگترین شگفتی برای شما این خواهد بود که پس از یادگیری بخش عمده سینتکس زبان، متوجه میشوید قدرت واقعی آن نه در تعداد قابلیتها، بلکه در حذف هوشمندانه پیچیدگیها پنهان شده است.