Go dilinde kompozisyon desteği embedding mekanizması aracılığıyla sağlanır — bu, bir yapının başka bir yapının içine açık bir alan adı olmadan dahil edilmesi imkanıdır. Bu yaklaşımın, mirası "kendi tarzında" modellemeye olanak tanıması ve dilin sadeliğini koruyarak çoklu mirasla ilgili birçok karmaşıklıktan kaçınması amaçlanmıştır.
Geliştirici genellikle belirli bir temel yapının davranışını veya arayüzünü karmaşayı artırmadan genişletmek ister. Go'da bunun için genellikle embedding kullanılır; bu, iç içe geçen türün yöntemlerine ve alanlarına doğrudan erişim sağlar, sanki bunlar üst yapı içinde tanımlanmış gibi. Ancak embedding'in kendine has özellikleri vardır ve mekanizmanın yanlış anlaşılması, isim çatışmaları, double embedding ve yöntemlerin yanlış miras alınması gibi beklenmedik hatalara yol açabilir.
Embedding'in doğru ve ihtiyatlı kullanımı daha temiz bir kompozisyon elde etmeyi sağlar. Yöntemlerin ve alanların "bir üst seviyeye" çıkarıldığını ve embedding'in aslında "has-a" (sahiplik) ilişkisini gerçekleştirdiğini hatırlamak gerekir; "is-a" (bir tür olma) ilişkisi değildir. İsim çatışmalarından kaçınılmalı ve yöntem alıcısının nasıl çalıştığına dair bir anlayışa sahip olunmalıdır.
Kod örneği:
package main import "fmt" type Engine struct { Power int } func (e Engine) Start() { fmt.Println("Motor güçle başladı", e.Power) } type Car struct { Engine // embedding, Engine değil engine Engine gibi bir alan Brand string } func main() { c := Car{Engine: Engine{Power: 200}, Brand: "Toyota"} c.Start() // doğrudan erişilebilir fmt.Println(c.Power) // alan da kaldırılmıştır }
Anahtar özellikler:
Embedding çoklu mirası gerçekleştirebilir mi?
Hayır, embedding OOP'deki gibi miras sağlamaz; bu bir kompozisyondur. Bir yapıya birden fazla başka yapı eklenebilir, ancak yöntemler çakıştığında bir birleşim değil, çakışma hatası meydana gelir.
İç içe geçmiş yapıların alanları aynı isimlere sahipse ne olur?
Doğrudan erişimde bir derleme hatası meydana gelir: derleyici hangi alana ulaşacağını bilmez. Gerekirse iç içe geçmiş yapının adı aracılığıyla yol açıkça belirtilmelidir.
Kod örneği:
type A struct {X int} type B struct {X int} type C struct { A B } func main() { c := C{} // c.X = 1 // hata: ambiguous selector c.A.X = 1 // sadece böyle }
Embedding'de yöntemler değerle/göstericiyle aynı şekilde mi yükseltilir?
Hayır. Gösterici alıcıya sahip yöntemler yalnızca yapı da gösterici kullanıyorsa yükseltilir (c := &Car{}). Yapı değer olarak kullanıldığında, gösterici alıcıya sahip yöntemler "yükseltilmeyecek".
Derin iç içe yapı kullanılan bir proje, burada embedding çok katmanlı mirası taklit etmek için kullanılıyor. Yeni bir geliştirici, alanın veya yöntemin hangi yapından geldiğini anlamıyor ve tüm proje karmaşık hale geliyor ve "büyülü" bir hale geliyor.
Artılar:
Eksiler:
Bir arayüzün uygulanması için embedding kullanılıyor; örneğin, Logger arayüzü, Println yöntemine sahip bir tür aracılığıyla embedding ile uygulanıyor ve bu açıkça belgelenmiş ve testlerle kapsanmıştır.
Artılar:
Eksiler: