Konu geçmişi: Alıcı yöntemler, Go'da arayüzlerin uygulanabilir olmasını ve kendi türleri için davranış kapsülleme sağlamayı mümkün kılmak amacıyla ortaya çıktı. Bu, OOP dillerindeki sınıf yöntemlerine benzerdir.
Sorun: Go’da yöntemler, yapı (veya diğer türler) için iki şekilde bildirilebilir: değer alıcı veya işaretçi alıcı aracılığıyla. Bu iki türün farklılıklarının yanlış uygulanması, özellikle yöntemlerin alıcı türüne ve çağrılma şekline (değişken veya işaretçi üzerinden) bağlı olarak sezgisel olmayan hatalara yol açabilir.
Çözüm:
Değer alıcı ile tanımlanan bir yöntem çağrıldığında, tüm yapı kopyalanır ve bu tür bir yöntem içindeki değişiklikler başlangıç nesnesini etkilemez. İşaretçi alıcı orijinal nesneyle çalışmayı ve değişiklik yapmayı sağlar. Doğru alıcının seçilmesi, performans optimizasyonu ve doğru davranış açısından önemlidir.
Kod örneği:
package main import "fmt" type Counter struct { Value int } func (c Counter) IncByValue() { // alıcı — değer c.Value++ } func (c *Counter) IncByPointer() { // alıcı — işaretçi c.Value++ } func main() { c := Counter{} c.IncByValue() fmt.Println(c.Value) // 0 yazdırır c.IncByPointer() fmt.Println(c.Value) // 1 yazdırır }
Temel özellikler:
1. Eğer yapı büyük alanlar içeriyorsa (örneğin, bir dizi [1000]int), bu yöntem için hangi alıcıyı kullanmak daha iyidir ve neden?
Cevap: Büyük veri kopyalama maliyetinden kaçınmak için işaretçi alıcıyı kullanmak daha iyidir. Değer alıcı ile tanımlanan bir yöntem tüm nesneyi kopyalayacaktır, bu da verimsizdir.
2. İşaretçi alıcıya sahip bir yapı, değer alıcı ile yöntemler tanımlayan bir arayüz ile uyumlu mudur?
Cevap: Hayır. Eğer bir arayüz yönteminin değere dayanarak tanımlanmışsa ve yapı bunu sadece işaretçi ile gerçekleştiriyorsa, derleyici uyumlu saymayacaktır.
3. İşaretçi alıcıya sahip bir yöntem, değer değişkeni üzerinde (işaretçi yerine) çağrılabilir mi?
Cevap: Evet. Go, adresi otomatik olarak (&struct) alır, yani çağrıyı doğru bir şekilde yapar.
c := Counter{} c.IncByPointer() // Go (&c).IncByPointer() çağrısını yapacaktır
Projede yapı çok büyük, ancak tüm yöntemler değer üzerinde tanımlanmış (value receiver). Her çağrıda tüm nesne kopyalanıyor, bu da performansta dikkate değer bir durum yaratıyor.
Artılar: Basitlik, başlangıç nesnesini yanlışlıkla değiştirme olasılığı yok. Eksiler: Yüksek bellek ve işlemci maliyetleri.
Küçük bir yapı için, büyük bir durumu olmayan yöntemler değer üzerinde tanımlanır, büyük yapılar için yalnızca işaretçi üzerinde tanımlanır. Nesneyi değiştiren yöntemler, işaretçiler ile kullanılır.
Artılar: Bellek tasarrufu, durumun doğru bir şekilde değiştirilmesi. Eksiler: Arayüzlerle uyumluluğun takip edilmesi ve işaretçilerin aktarım özellikleri hakkında bilgi sahibi olunması gerekiyor.