ProgramlamaGo разработчик

Go'daki değer (value) ve işaretçi (pointer) alıcılarıyla (receivers) yöntemlerin nasıl çalıştığı, aralarında seçim yaparken hangi prensiplerin göz önünde bulundurulması gerektiği ve arayüzlerle (interfaces) ilişkili davranışlarda hangi tuzakların bulunduğu?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

Go'da yöntemler hem değer (value) hem de türün işaretçisi (pointer) için ilan edilebilir. Bu özellik, verilerin kimler tarafından değiştirileceğine açık bir kontrol sağlamak için dilin erken sürümlerinden beri korunmuştur. Klasik sorun, değer (değişmez) ve işaretçi (verilere ortak erişim ve değiştirme imkanı) semantiği arasındaki mesafeyi gerektirmektedir.

Sorun — Değer alıcı ile bir yöntemi ilan etmek ve beklenen etkiyi elde edememek ya da işaretçi değişkeninde değer yöntemini çağırmak kolaydır.

Çözüm — Aşağıdaki kurallara uyun:

  1. Bir yöntem, nesnenin durumunu değiştirmesi gerekiyorsa işaretçi (pointer) alıcı kullanın.
  2. Küçük değişmez yapılar için değer (value) alıcı kullanın.
  3. Arayüzler için genellikle tutarlılık adına işaretçi (pointer) alıcı tercih edilir.

Kod örneği:

type Counter struct { Value int } func (c Counter) IncCopy() { c.Value++ } // değer alıcı func (c *Counter) IncPointer() { c.Value++ } // işaretçi alıcı c := Counter{} c.IncCopy() // Değer 0 kalacak c.IncPointer() // Değer 1 olacak

Anahtar özellikler:

  • Değer alıcı, verilerin kopyasını garanti eder ve dışarıdan değiştirilmesini engeller.
  • İşaretçi alıcı, yapının iç durumunu değiştirmeye izin verir.
  • Arayüzler ve bunların uygulanması, alıcı türüne bağlıdır; bu durum atama sırasında beklenmedik sonuçlara yol açabilir.

Kandırmaca sorular.

Bir işaretçi üzerinde değer alıcı yöntemi ve bir değer üzerinde işaretçi yöntemini çağırmak mümkün mü?

Go, "kaputun altında" otomatik olarak işaretçileri derecelendirir veya adreslerini alır, bu nedenle çağrı uygun olduğunda yapılabilir. Ancak her zaman böyle olmaz — arayüzlerle bu aynı şekilde öngörülebilir değildir.

var c Counter (&c).IncCopy() // İşaretçi üzerinden değer yöntemini çağırabilirsiniz c.IncPointer() // İşaretçi yöntemini çağırabilirsiniz, Go otomatik olarak adresi alır

Eğer yapı yalnızca işaretçi yöntemlerini uyguluyorsa ama arayüzde değer olarak geçerse ne olur?

Bu tür bir nesne, işaretçi yöntemlerini gerektiriyorsa arayüzü uygulamaz, bu nedenle panic veya derleme hatası mümkündür.

type D interface { IncPointer() } func f(d D) {} c := Counter{} f(c) // hata! Counter değeri arayüzü uygulamaz f(&c) // doğru

İşaretçi alıcı yöntemi çağrıldığında, eğer işaretçinin kopyası geçtilerse yapı değişir mi?

Evet, işaretçi kopyalansa bile altındaki nesne aynı kalır — sonuç birbirinin aynısı olacaktır.

c := Counter{} p := &c p2 := p p2.IncPointer() // Değer artar

Tipik hatalar ve anti-paternler

  • Yanlış alıcı ile yöntemler ilan etmek ve bir kopya üzerinden yapıyı değiştirmeye çalışmak.
  • Büyük yapılar için değer alıcı kullanmak — gereksiz kopyalama.
  • Alıcıdan kaynaklanan arayüz uyum hataları.

Hayattan bir örnek

Olumsuz vaka

Mühendis, "Update" yöntemleri ile değer alıcı içeren bir yapı uygular. Arayüz üzerinden yapı geçildiğinde, değişiklikler "kaybolur" — çünkü kopya ile çalışılır.

Artıları:

  • Yapının temiz değişmezliği.

Eksileri:

  • Değişiklik beklentisi ama değişiklik olmaması — hatayı takip etmek zor.

Olumlu vaka

Ekipte açık bir anlaşma: durum değişikliğini içeren tüm yöntemler yalnızca işaretçi alıcıdır, arayüzler yalnızca işaretçilerle uygulanır, değer — "genişletmeler" ve yardımcılar için.

Artıları:

  • Belirsizlik yok, minimum sürprizler.

Eksileri:

  • Türlere dikkat edilmediğinde hatanın nedenini anlamak bazen zor.