در این بخش ما یکسری سوالات مصاحبه به زبان فارسی ارائه دادیم که برای یافتن ضعفها کاربردی میباشد.
1: چه تایپهایی مقدار zero آنها nil است؟
- interfaces
- slices
- channels
- maps
- pointers
- functions
2: تایپهای نوع Reference؟
- Pointers
- slices
- maps
- functions
- channels
3: تایپهای نوع Aggregate؟
4: چه وقت باید از پوینتر استفاده کنیم؟
1- تابعی که یکی از پارامترهای خود را تغییر میدهد
-وقتی تابعی را فراخوانی میکنیم که یک پوینتر را به عنوان پارامتر میگیرد، انتظار داریم که متغیر ما تغییر داده شود. اگر شما متغیر را در تابع خود تغییر نمیدهید، پس احتمالا نباید از پوینتر استفاده کنید.
2- عملکرد بهتر
-اگر رشتهای داشته باشید که شامل یک رمان کامل در حافظه باشد، کپی کردن این متغیر هر بار که به یک تابع جدید ارسال میشود، کاری بسیار گران است. ممکن است ارزشمند باشد که به جای این کار یک پوینتر را ارسال کنید، که باعث صرفهجویی در پردازنده و حافظه میشود. با این حال انجام این کار به قیمت خوانا بودن است، بنابراین فقط در صورت لزوم این بهینهسازی را انجام دهید.
3- به گزینه nil نیاز دارید
-گاهی اوقات یک تابع باید بداند که مقدار یک چیزی چیست، همچنین باید وجود یا عدم وجود آن را بداند. معمولا هنگام خواندن JSON از این استفاده میکنیم تا بدانیم فیلدی وجود دارد یا خیر.
5: زبان گولنگ از موارد زیر پشتیبانی نمیکند؟
- type inheritance
- operator overloading
- method overloading
- pointer arithmetic
- struct type in consts
6: برای گوروتین ها چه مواقعی از channel و چه مواقعی از mutex استفاده می شود؟
معمولاً در مواقعی که گوروتین ها نیاز به برقراری ارتباط با یکدیگر دارند، از channels استفاده می کنیم. درصورتی که قسمتی از کد ما(برای مثال مقدار متغیری را تغییر می دهیم) که در آن واحد فقط باید یک گوروتین به آن دسترسی داشته باشد، از یک قفل مانند mutext استفاده می کنیم.
7: تفاوت بین goroutine و thread را توضیح دهید.
Goroutines سبک وزن هستند و دارای یک استک اولیه کوچکتر که به صورت پویا گسترش مییابد هستند، این در حالی است که threads استک ثابت دارند. Goroutines همزمانی را در سطح زبان با استفاده از channelها مدیریت میکنند، در حالی که threads ممکن است نیاز به lockهای صریح داشته باشند. همچنین، سوئیچینگ بین goroutines کارایی بیشتری نسبت به thread switching دارد.
8: توضیح دهید که interface در Golang چیست و چگونه میتواند مورد استفاده قرار گیرد.
یک interface در Go یک نوع خاص است که مجموعهای از method signatures را تعریف میکند. هر نوع دادهای که این متدها را پیادهسازی کند، میتواند به عنوان آن interface مورد استفاده قرار گیرد. این بدون نیاز به ارث بری صورت میگیرد و امکان داکتایپینگ را فراهم میکند.
9: چطور میتوانید memory leak در برنامههای نوشته شده به وسیله Golang را شناسایی و مدیریت کنید؟
برای شناسایی memory leaks در Golang، میتوان از ابزارهایی مانند pprof به همراه نمودارهای ساخته شده بر اساس heap dumps استفاده کرد. برای پیشگیری از memory leaks، باید دقت کرد که از دادهها به درستی استفاده شود، حافظه رزرو شده آزاد گردد و منابع بسته شوند هنگامی که دیگر نیازی به آنها نیست.
10: در Golang چگونه میتوانیم dependency management را انجام دهیم؟
Golang از Go Modules برای مدیریت وابستگیها استفاده میکند که به توسعه دهندگان امکان میدهد پروژهها را به صورت مستقل از GOPATH راحتتر مدیریت کنند. با استفاده از دستوراتی مانند go mod init
, go mod tidy
, و go mod vendor
میتوان وابستگیهای لازم برای پروژه را مدیریت کرد.
11: چه زمانی یک channel در Golang باید با buffer مورد استفاده قرار گیرد؟
یک channel با buffer زمانی مورد استفاده قرار گیرد که شما میخواهید ارتباطات بین goroutines را بدون ایجاد blocking فوری داشته باشید. این امر میتواند بازده کدها را در مواقعی که عملیاتها از لحاظ عملکرد اندکی نابرابر هستند، بهبود بخشد.
12: توضیح دهید که defer statement چیست و چرا ممکن است از آن استفاده کنیم.
Defer statement برای تضمین اجرای یک تابع مشخص، درست قبل از خارج شدن از تابع فعلی استفاده میشود. این برای راحتی در مدیریت منابع مثل بستن فایلها و ارتباطات شبکه استفاده میشود که میخواهیم اطمینان حاصل کنیم که بهدرستی بسته خواهند شد.
13: در Golang چگونه میتوانید اطمینان حاصل کنید که یک goroutine نتیجهای تولید میکند قبل از اینکه برنامه کار خود را به طور کامل متوقف کند؟
برای اطمینان از اینکه یک goroutine کار خود را به اتمام برساند، معمولا از sync.WaitGroup برای همچین مدیریتی استفاده میکنیم. ساختار WaitGroup اجازه میدهد تا اصلیترین goroutine صبر کند تا یک یا چند goroutines دیگر کار خود را تمام کنند.
14: توضیح دهید که واحد ایزوله برای کد نویسی در Golang چیست (table-driven tests) و چرا مفید است.
Table-driven tests شیوهای برای نوشتن تستها است که از جداول داده برای تعریف چندین case تست بهره میبرد. این شیوه مفید است زیرا کد تست را میتوان برای بسیاری از دادهها به راحتی توسعه داد و به خوبی سازماندهی میشود.
15: چرا Go از ارث بری (inheritance) پشتیبانی نمیکند و از composition به عنوان جایگزین استفاده میکند؟
Go ارث بری را پیادهسازی نمیکند زیرا میتواند پیچیده شود و معماری نرمافزار را سختتر مدیریت کند. در عوض، از composition استفاده میکند که میتواند code reuse را تشویق کند و طراحی سیستم را سادهتر و ماژولارتر میکند.
16: آیا در Golang میتوان از polymorphism استفاده کرد؟ اگر بله، چگونه؟
بله، در Go میتوان از polymorphism استفاده کرد از طریق استفاده از interfaces. یک interface میتواند برای تعریف یک مجموعه از روشها به کار رود و هر نوع که این روشها را پیادهسازی کند به عنوان آن نوع interface شناخته شود.
17: چه تفاوتی میان make و new در Golang وجود دارد؟
make
در Go برای ایجاد sliceها، maps و channels استفاده میشود و یک ابجکت از نوع مورد نظر را با مقدار اولیه مشخصی برمیگرداند. از طرفی new
یک pointer به یک ابجکت از یک نوع دادهای تعریف شده توسط کاربر را برمیگرداند که صفر اولیه شده است.
18: متود (method) receivers در Golang چگونه کار میکند و تفاوت بین استفاده از pointer receiver و value receiver چیست؟
Method receivers در Go اجازه میدهند تا روی نوع معینی از مقادیر عملیات انجام دهیم. استفاده از pointer receiver به ما اجازه میدهد تا تغییراتی که در method روی receiver اعمال میشوند را بر روی خود آبجکت اصلی اعمال کنیم، در حالیکه استفاده از value receiver یک کپی از مقدار را میگیرد و تغییرات او روی کپی صورت میگیرد و بر آبجکت اصلی اثر نمیگذارد.
19: چگونه میتوان در Golang یک پکیج اختصاصی ایجاد کرد و چگونه میتوان آن را در دیگر فایلهای Go مورد استفاده قرار داد؟
برای ایجاد پکیج اختصاصی در Go، کد مربوطه باید در یک دایرکتوری قرار داده شود و بالای فایلهای Go باید package mypackage
تعریف شود. برای استفاده از پکیج، import "path/to/mypackage"
باید در دیگر فایلها قرار داده شود.
20: در Golang، چگونه میتوانید error handling را اجرا کنید و چه روشهایی برای پیادهسازی custom error types وجود دارد؟
Error handling در Go اغلب از طریق بازگرداندن ارور از توابع و بررسی آنها انجام میشود. برای ایجاد custom error types، میتوانید از errors.New()
برای ایجاد یک ارور ساده استفاده کنید یا یک تایپ که ارور را پیادهسازی میکند با متدهای اضافی برای دادههای اضافی مرتبط با ارور ایجاد کرد.
21: کامپایلر گولنگ از نوع AOT است یا JIT؟ تفاوت بین AOT و JIT را بگو.
کامپایلر گو یک Ahead Of Time compilation است. تفاوت AOT با JIT در این است که کامپایلر های AOT مستقیم کد ما را تبدیل به machine code می کنند اما در کامپایتر های JIT کد ما تبدیل به یک کد میانی Bytecode می شود و در زمان اجرا توسط runtime engine هر قسمت از برنامه که مورد استفاده قرار می گیرد، تفسیر می شود و تبدیل به machine code می شود.