Menu
Coddy logo textTech

Generic Struct'lar

Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 70 / 107.

Tıpkı fonksiyonlar gibi, struct'lar da tür parametrelerine sahip olabilir. Bir generic struct, tür güvenliğini korurken herhangi bir türle çalışan veri yapıları oluşturmanıza olanak tanır. Bu; yığınlar, kuyruklar veya anahtar-değer çiftleri gibi yeniden kullanılabilir kapsayıcılar oluşturmak için mükemmeldir.

Genel (generic) bir struct tanımlamak için, tür parametresini struct adından sonra yerleştirin:

type Box[T any] struct {
    Value T
}

func main() {
    intBox := Box[int]{Value: 42}
    strBox := Box[string]{Value: "hello"}
    
    fmt.Println(intBox.Value)  // 42
    fmt.Println(strBox.Value)  // hello
}

Generic fonksiyonların aksine, bir generic struct örneği oluştururken türü açıkça belirtmelisiniz. Go, türü tek başına bağlamdan çıkaramaz.

Generic struct'lar birden fazla tip parametresine sahip olabilir, bu da onları çiftler (pairs) veya eşlemeler (maps) gibi yapılar için ideal hale getirir:

type Pair[K, V any] struct {
    Key   K
    Value V
}

p := Pair[string, int]{Key: "age", Value: 25}
fmt.Printf("%s: %d\n", p.Key, p.Value)  // age: 25

Depolanan türlerin belirli işlemleri desteklemesini sağlamak için kısıtlamalar da uygulayabilirsiniz:

type NumberBox[T int | float64] struct {
    Value T
}

nb := NumberBox[float64]{Value: 3.14}

Jenerik yapılar, Go'da tip güvenli ve yeniden kullanılabilir veri yapıları oluşturmanın temelini oluşturur. Bir sonraki derste, bu jenerik tipler üzerinde metotların nasıl tanımlanacağını öğreneceksiniz.

challenge icon

Görev

Kolay

Farklı türdeki öğeleri takip edebilen genel (generic) bir envanter sistemi kuralım! Tam tür güvenliğini korurken her türle çalışan yeniden kullanılabilir konteyner yapıları oluşturacaksınız.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • inventory.go: Genel konteyner yapılarınızı tanımlayın.

    İki alanı olan genel bir struct Item[T any] oluşturun: Name (string) ve Data (T türünde). Bu, esnek bir türdeki ilişkili veriye sahip herhangi bir öğeyi temsil eder.

    Item[T] dilimini (slice) tutan tek bir Items alanına sahip genel bir struct Container[T any] oluşturun. Bu konteyner, aynı veri türündeki birden fazla öğeyi saklayabilir.

    Üç alanı olan kısıtlanmış genel bir struct PricedItem[T int | float64] oluşturun: Name (string), Quantity (int) ve Price (T türünde). Kısıtlama, fiyatların her zaman sayısal olmasını sağlar.

    Boş bir Container oluşturup ona bir işaretçi (pointer) döndüren bir NewContainer[T any]() *Container[T] fonksiyonu uygulayın.

    *Container[T] üzerinde bir isim (string) ve veri (T) alan, bir Item oluşturan ve bunu konteynerin Items dilimine ekleyen bir Add metodu uygulayın.

    Container[T] üzerinde konteynerdeki öğe sayısını döndüren bir Count metodu uygulayın.

  • main.go: Genel yapılarınızı farklı türlerle gösterin.

    Bir öğe türü (string, int veya priced) okuyun, ardından bir sayı ve ardından öğe detaylarını okuyun.

    string türü için: İsim ve string veri çiftlerini okuyun. Bir Container[string] oluşturun, tüm öğeleri ekleyin, ardından her öğeyi [Name]: [Data] şeklinde ve ardından toplam sayıyı yazdırın.

    int türü için: İsim ve tam sayı veri çiftlerini okuyun. Bir Container[int] oluşturun, tüm öğeleri ekleyin, ardından her öğeyi [Name]: [Data] şeklinde ve ardından toplam sayıyı yazdırın.

    priced türü için: İsim, miktar ve fiyat (float olarak) üçlülerini okuyun. Doğrudan PricedItem[float64] örnekleri oluşturun ve her birini fiyat bir ondalık basamak gösterecek şekilde [Name] x[Quantity] @ [Price] olarak yazdırın.

    Toplam sayı satırını şu şekilde yazdırın: Total items: [count]

Aşağıdaki girdiler sağlanacaktır:

  • Satır 1: Öğe türü (string, int veya priced)
  • Satır 2: Öğe sayısı (tam sayı)
  • Sonraki satırlar: Türe göre öğe detayları

Örneğin, şu girdi verildiğinde:

string
3
Book
Fiction Novel
Pen
Blue Ink
Notebook
Lined Paper

Çıktınız şöyle olmalıdır:

Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3

Ve şu girdi verildiğinde:

int
2
Apples
50
Oranges
30

Çıktınız şöyle olmalıdır:

Apples: 50
Oranges: 30
Total items: 2

Ve şu girdi verildiğinde:

priced
2
Widget
10
19.99
Gadget
5
49.50

Çıktınız şöyle olmalıdır:

Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2

Aynı Container yapısının hem string hem de tam sayılarla nasıl sorunsuz çalıştığına, PricedItem yapısının ise fiyatlar için yalnızca sayısal türlerin kullanılabilmesini sağlamak için nasıl bir kısıtlama kullandığına dikkat edin. Bu genel struct'ların örneklerini oluştururken tür parametresini açıkça belirtmelisiniz.

Kopya kağıdı

Bir generic struct (genel yapı), tip güvenliğini korurken herhangi bir tiple çalışan veri yapıları oluşturmanıza olanak tanır.

Generic bir struct tanımlamak için, tip parametresini struct adından sonra yerleştirin:

type Box[T any] struct {
    Value T
}

Generic bir struct örneği oluştururken, tipi açıkça belirtmelisiniz (Go bunu çıkarım yoluyla belirleyemez):

intBox := Box[int]{Value: 42}
strBox := Box[string]{Value: "hello"}

Generic struct'lar birden fazla tip parametresine sahip olabilir:

type Pair[K, V any] struct {
    Key   K
    Value V
}

p := Pair[string, int]{Key: "age", Value: 25}

Depolanan tiplerin belirli işlemleri desteklemesini sağlamak için kısıtlamalar uygulayabilirsiniz:

type NumberBox[T int | float64] struct {
    Value T
}

nb := NumberBox[float64]{Value: 3.14}

Generic struct'lar, dilimler (slices) dahil olmak üzere tip parametresini kullanan alanlar içerebilir:

type Container[T any] struct {
    Items []T
}

Generic struct'lar üzerinde metotlar tanımlayabilir ve generic yapıcı (constructor) fonksiyonlar oluşturabilirsiniz:

func NewContainer[T any]() *Container[T] {
    return &Container[T]{Items: []T{}}
}

func (c *Container[T]) Add(item T) {
    c.Items = append(c.Items, item)
}

Kendin dene

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Öğe türünü oku
	scanner.Scan()
	itemType := scanner.Text()
	
	// Sayısı oku
	scanner.Scan()
	count, _ := strconv.Atoi(scanner.Text())
	
	switch itemType {
	case "string":
		// TODO: NewContainer kullanarak bir Container[string] oluşturun
		// TODO: 'count' kadar öğe okuyun (her birinin adı bir satırda, verisi sonraki satırda)
		// TODO: Her öğeyi konteynere ekleyin
		// TODO: Her öğeyi "[Name]: [Data]" şeklinde yazdırın
		// TODO: Count metodunu kullanarak "Total items: [count]" yazdırın
		
	case "int":
		// TODO: NewContainer kullanarak bir Container[int] oluşturun
		// TODO: 'count' kadar öğe okuyun (her birinin adı bir satırda, tam sayı verisi sonraki satırda)
		// TODO: Her öğeyi konteynere ekleyin
		// TODO: Her öğeyi "[Name]: [Data]" şeklinde yazdırın
		// TODO: Count metodunu kullanarak "Total items: [count]" yazdırın
		
	case "priced":
		// TODO: PricedItem[float64] örneklerini tutmak için bir slice oluşturun
		// TODO: 'count' kadar öğe okuyun (her birinin adı, miktarı ve fiyatı ayrı satırlarda)
		// TODO: Her biri için PricedItem[float64] oluşturun ve slice'a ekleyin
		// TODO: Her öğeyi, fiyat bir ondalık basamak gösterecek şekilde "[Name] x[Quantity] @ [Price]" olarak yazdırın
		// TODO: "Total items: [count]" yazdırın
	}
	
	_ = scanner // Girdiyi okumak için scanner kullanın
	_ = count   // Döngü için count kullanın
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler