Go Struct
Struct farklı türlerde verileri depolayabilen bileşik bir türdür. Go sınıf ve kalıtımı bıraktı, aynı zamanda yapıcı metotları da bıraktı, nesne yönelimli işlevleri bilinçli olarak zayıflattı. Go geleneksel bir OOP dili değildir, ancak Go'nun hala OOP'den izleri vardır, struct ve metodlar ile bir sınıf simüle edilebilir. Aşağıda basit bir struct örneği verilmiştir:
type Programmer struct {
Name string
Age int
Job string
Language []string
}Tanımlama
Struct tanımlama oldukça basittir, örnek:
type Person struct {
name string
age int
}Struct'ın kendisi ve içindeki alanlar büyük/küçük harf isimlendirme kurallarına uyar. Aynı türdeki komşu alanlar için türü tekrar tekrar tanımlamanıza gerek yoktur:
type Rectangle struct {
height, width, area int
color string
}TIP
Struct alanı tanımlarken, alan adı metod adıyla aynı olamaz.
Örnekleme
Go'da yapıcı metod yoktur. Çoğu durumda struct'ı örnekleme aşağıdaki gibi yapılır. Başlatma sırasında map gibi alan adlarını belirtip ardından alan değerlerini başlatırsınız:
programmer := Programmer{
Name: "jack",
Age: 19,
Job: "coder",
Language: []string{"Go", "C++"},
}Ancak alan adlarını atlayabilirsiniz. Alan adlarını atlattığınızda, tüm alanları başlatmanız gerekir. Genellikle bu yöntemi kullanmanız önerilmez, çünkü okunabilirlik çok kötüdür.
programmer := Programmer{
"jack",
19,
"coder",
[]string{"Go", "C++"}}Eğer örnekleme süreci karmaşıksa, struct'ı örnekleme için bir fonksiyon yazabilirsiniz. Aşağıdaki gibi, bunu bir yapıcı fonksiyon olarak da düşünebilirsiniz:
type Person struct {
Name string
Age int
Address string
Salary float64
}
func NewPerson(name string, age int, address string, salary float64) *Person {
return &Person{Name: name, Age: age, Address: address, Salary: salary}
}Ancak Go fonksiyon ve metod aşırı yüklemeyi desteklemez, bu yüzden aynı fonksiyon veya metod için farklı parametreler tanımlayamazsınız. Struct'ı birden fazla şekilde örnekleme isterseniz, ya birden fazla yapıcı fonksiyon oluşturun ya da options desenini kullanmanız önerilir.
Options Deseni
Options deseni Go dilinde çok yaygın bir tasarım desenidir, struct'ı daha esnek bir şekilde örnekleme için kullanılır, genişletilebilirdir ve yapıcı fonksiyonun fonksiyon imzasını değiştirmeyi gerektirmez. Aşağıdaki gibi bir struct olduğunu varsayalım:
type Person struct {
Name string
Age int
Address string
Salary float64
Birthday string
}PersonOptions türünü tanımlayın, *Person türünde bir parametre kabul eder. Pointer olmalıdır, çünkü Person'a atama yapmak için closure içinde kullanacağız.
type PersonOptions func(p *Person)Ardından options fonksiyonlarını oluşturun, genellikle With ile başlarlar. Dönüş değerleri bir closure fonksiyonudur.
func WithName(name string) PersonOptions {
return func(p *Person) {
p.Name = name
}
}
func WithAge(age int) PersonOptions {
return func(p *Person) {
p.Age = age
}
}
func WithAddress(address string) PersonOptions {
return func(p *Person) {
p.Address = address
}
}
func WithSalary(salary float64) PersonOptions {
return func(p *Person) {
p.Salary = salary
}
}Gerçekten tanımlanan yapıcı fonksiyon imzası şöyledir, değişken uzunlukta PersonOptions türünde parametre kabul eder:
func NewPerson(options ...PersonOptions) *Person {
// önce options'ları uygula
p := &Person{}
for _, option := range options {
option(p)
}
// varsayılan değer işleme
if p.Age < 0 {
p.Age = 0
}
......
return p
}Bu şekilde farklı örnekleme gereksinimleri için sadece bir yapıcı fonksiyon ile tamamlanabilir, sadece farklı Options fonksiyonları girmeniz gerekir:
func main() {
pl := NewPerson(
WithName("John Doe"),
WithAge(25),
WithAddress("123 Main St"),
WithSalary(10000.00),
)
p2 := NewPerson(
WithName("Mike jane"),
WithAge(30),
)
}Fonksiyonel options deseni birçok açık kaynak projede görülebilir, gRPC Server örnekleme yöntemi de bu tasarım desenini kullanır. Fonksiyonel options deseni sadece karmaşık örnekleme için uygundur. Eğer parametreler sadece birkaç basit tane ise, normal yapıcı fonksiyon kullanmanız önerilir.
Kompozisyon
Go'da struct'lar arasındaki ilişkiler kompozisyon ile ifade edilir. Açık kompozisyon veya anonim kompozisyon olabilir. İkincisi kullanımı kalıtıma daha benzer, ancak doğası gereği hiçbir değişiklik yoktur. Örneğin:
Açık kompozisyon yöntemi:
type Person struct {
name string
age int
}
type Student struct {
p Person
school string
}
type Employee struct {
p Person
job string
}Kullanırken açıkça p alanını belirtmeniz gerekir:
student := Student{
p: Person{name: "jack", age: 18},
school: "lili school",
}
fmt.Println(student.p.name)Anonim kompozisyon açıkça alan belirtmenizi gerektirmez:
type Person struct {
name string
age int
}
type Student struct {
Person
school string
}
type Employee struct {
Person
job string
}Anonim alanın adı varsayılan olarak tür adıdır. Çağırıcı bu türün alan ve metodlarına doğrudan erişebilir. Ancak ilk yöntemden daha uygun olmasının dışında hiçbir farkı yoktur.
student := Student{
Person: Person{name: "jack",age: 18},
school: "lili school",
}
fmt.Println(student.name)Pointer
Struct pointer için, struct içeriğine erişmek için referanssız hale getirmeye gerek yoktur. Örnek:
p := &Person{
name: "jack",
age: 18,
}
fmt.Println(p.age,p.name)Derleme sırasında (*p).name, (*p).age'ye dönüştürülür. Aslında hala referanssız hale getirme gerekir, ancak kodlama sırasında bundan kaçınabilirsiniz, bu bir söz dizimi şekeri sayılır.
Tag
Struct tag bir meta programlama formudur, yansıma ile birleştirilerek birçok harika işlevsellik oluşturulabilir. Format şöyledir:
`key1:"val1" key2:"val2"`Tag bir anahtar-değer çifti formundadır, boşluk ile ayrılır. Struct tag'in hata toleransı çok düşüktür. Eğer struct doğru formatta yazılmazsa, normal şekilde okunamaz. Ancak derleme sırasında hiçbir hata vermez. Aşağıda bir kullanım örneği verilmiştir:
type Programmer struct {
Name string `json:"name"`
Age int `yaml:"age"`
Job string `toml:"job"`
Language []string `properties:"language"`
}Struct tag en yaygın olarak çeşitli serileştirme formatlarında takma ad tanımlamak için kullanılır. Tag kullanımı yansıma ile birleştirilerek işlevselliğini tam olarak ortaya çıkarabilir.
Bellek Hizalama
Go struct alanlarının bellek dağılımı bellek hizalama kurallarına uyar. Bu CPU'nun belleğe erişim sayısını azaltır, buna karşılık kaplanan bellek biraz daha fazla olur. Bu alan-zaman takası yöntemidir. Aşağıdaki struct olduğunu varsayalım:
type Num struct {
A int64
B int32
C int16
D int8
E int32
}Bu türlerin kapladığı bayt sayısı bilinmektedir:
int648 bayt kaplarint324 bayt kaplarint162 bayt kaplarint81 bayt kaplar
Tüm struct'ın bellek kullanımı görünüşte 8+4+2+1+4=19 bayt mı? Elbette böyle değil. Bellek hizalama kurallarına göre, struct'ın bellek kullanım uzunluğu en az en büyük alanın katı olmalıdır, eksik olanlar tamamlanır. Bu struct'ta en büyük int64 8 bayt kaplar, bu yüzden bellek dağılımı aşağıdaki şekilde gösterilmiştir:

Bu yüzden aslında 24 bayt kaplar, bunlardan 5 bayt kullanışsızdır.
Aşağıdaki struct'a bakalım:
type Num struct {
A int8
B int64
C int8
}Yukarıdaki kuralı anladıktan sonra, bellek kullanımının 24 bayt olduğunu hızlı bir şekilde anlayabilirsiniz. Sadece üç alanı olmasına rağmen, 14 bayt israf edilir.

Ancak alanları aşağıdaki sıraya göre ayarlayabiliriz:
type Num struct {
A int8
C int8
B int64
}Bu şekilde kaplanan bellek 16 bayta düşer, 6 bayt israf edilir, 8 bayt bellek israfı azaltılır.

Teorik olarak, struct'taki alanların makul bir sıraya göre dağıtılması bellek kullanımını azaltabilir. Ancak gerçek kodlama sürecinde, bunu yapmak için gerekli bir neden yoktur. Bellek kullanımını azaltma konusunda mutlaka somut bir iyileştirme getirmeyebilir, ancak geliştiricilerin stresini ve bilişsel yükünü mutlaka artırır. Özellikle iş bazı struct alanları onlarca veya yüzlerce olabilir, bu yüzden sadece bilgi edinmek yeterlidir.
TIP
Eğer gerçekten bu yöntemle bellek tasarrufu yapmak isterseniz, şu iki kütüphaneye bakabilirsiniz:
Bu kütüphaneler kaynak kodunuzdaki struct'ları kontrol eder, struct alanlarını hesaplar ve yeniden düzenleyerek struct'ın kapladığı belleği en aza indirir.
Boş Struct
Boş struct alanı yoktur, bellek kaplamaz. unsafe.SizeOf fonksiyonu ile kapladığı bayt boyutunu hesaplayabiliriz:
func main() {
type Empty struct {}
fmt.Println(unsafe.Sizeof(Empty{}))
}Çıktı:
0Boş struct'ın kullanım senaryoları çoktur. Örneğin daha önce belirtildiği gibi, map değer türü olarak kullanılarak map set olarak kullanılabilir. Veya channel türü olarak, sadece bildirim türü channel olarak kullanılabilir.
