Skip to content

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:

go
type Programmer struct {
  Name     string
  Age      int
  Job      string
  Language []string
}

Tanımlama

Struct tanımlama oldukça basittir, örnek:

go
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:

go
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:

go
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.

go
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:

go
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:

go
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.

go
type PersonOptions func(p *Person)

Ardından options fonksiyonlarını oluşturun, genellikle With ile başlarlar. Dönüş değerleri bir closure fonksiyonudur.

go
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:

go
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:

go
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:

go
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:

go
student := Student{
   p:      Person{name: "jack", age: 18},
   school: "lili school",
}
fmt.Println(student.p.name)

Anonim kompozisyon açıkça alan belirtmenizi gerektirmez:

go
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.

go
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:

go
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:

go
`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:

go
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:

go
type Num struct {
  A int64
  B int32
  C int16
  D int8
    E int32
}

Bu türlerin kapladığı bayt sayısı bilinmektedir:

  • int64 8 bayt kaplar
  • int32 4 bayt kaplar
  • int16 2 bayt kaplar
  • int8 1 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:

go
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:

go
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:

go
func main() {
   type Empty struct {}
   fmt.Println(unsafe.Sizeof(Empty{}))
}

Çıktı:

0

Boş 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.

Golang by www.golangdev.cn edit