Menu
Coddy logo textTech

Kod Üretimine Genel Bakış

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

Kod üretimi, çalışma zamanında türleri incelemek yerine derleme zamanında Go kaynak kodu oluşturan, yansımaya (reflection) bir alternatiftir. Bu yaklaşım, tekrarlayan kalıplaşmış (boilerplate) kodları azaltırken size tür güvenliği ve daha iyi performans sağlar.

Go, kod üretimini otomatikleştirmek için go generate komutunu sağlar. Kaynak dosyanıza özel bir yorum yönergesi yerleştirirsiniz ve go generate komutunu çalıştırmak belirtilen aracı yürütür:

//go:generate stringer -type=Status

type Status int

const (
    Pending Status = iota
    Active
    Completed
)

go generate ./... komutunu çalıştırmak, Status tipi için bir String() metodu içeren yeni bir dosya oluşturan stringer aracını çağırır. Oluşturulan kod, programınızla birlikte derlenen normal Go kodudur.

Go ekosistemindeki yaygın kod oluşturma araçları arasında enum dize yöntemleri için stringer, arayüzlerden test mock'ları oluşturmak için mockgen ve Protocol Buffer tanımlarından kod oluşturmak için protoc bulunur. Birçok ORM ve API çerçevesi de tür güvenli veritabanı sorguları veya istemci kodu oluşturmak için kod oluşturmayı kullanır.

Yansıtma (reflection) üzerindeki temel avantaj, oluşturulan kodun derleyici tarafından kontrol edilmesidir. Hatalar çalışma zamanında değil, derleme zamanında yakalanır. Bunun karşılığındaki ödünleşim, fazladan bir derleme adımı ve deponuzda yönetilmesi gereken oluşturulmuş dosyalardır. Birçok türde tekrarlayan uygulamalar gerektiren OOP desenleri için, kod oluşturma genellikle güvenlik ve kolaylık arasındaki en iyi dengeyi sağlar.

challenge icon

Görev

Kolay

Enum benzeri türler için String() metotları oluşturan bir kod oluşturucu (code generator) inşa edelim! Bu ortamda gerçek bir go generate komutu çalıştıramasak da, kod oluşturma araçlarının ne yaptığını simüle edeceksiniz; yani tür tanımlarına dayalı olarak programlı bir şekilde Go kaynak kodu üreteceksiniz.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • generator.go: Kod oluşturma mantığınızı oluşturun.

    Bir Name (string) ve Values (sabit adlarını temsil eden string dilimi) içeren, enum benzeri bir tür tanımını temsil eden bir EnumType yapısı (struct) oluşturun.

    Bir String() metodu için Go kaynak kodu üreten bir GenerateStringMethod(e EnumType) string fonksiyonu oluşturun. Oluşturulan kod şu kalıbı izlemelidir:

    func (e TypeName) String() string {
        switch e {
        case Value1:
            return "Value1"
        case Value2:
            return "Value2"
        default:
            return "Unknown"
        }
    }

    Ayrıca iota kullanarak sabit bildirimlerini üreten bir GenerateConstants(e EnumType) string fonksiyonu oluşturun:

    const (
        Value1 TypeName = iota
        Value2
        Value3
    )

    Son olarak, her iki çıktıyı da bu kodun oluşturulmuş bir kod olduğunu belirten bir başlık yorumuyla birleştiren GenerateFullCode(e EnumType) string fonksiyonunu oluşturun:

    // Code generated by enum generator. DO NOT EDIT.
    
    [constants]
    
    [string method]
  • main.go: Farklı enum türleri için kod üretmek üzere oluşturucunuzu kullanın.

    Enum tür adını okuyun, ardından değer sayısını ve ardından her bir değer adını okuyun. Bu bilgileri oluşturucunuza aktarın ve oluşturulan kodun tamamını yazdırın.

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

  • Satır 1: Enum tür adı
  • Satır 2: Değer sayısı
  • Sonraki satırlar: Her bir değer adı

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

Status
3
Pending
Active
Completed

Çıktınız şu olmalıdır:

// Code generated by enum generator. DO NOT EDIT.

const (
    Pending Status = iota
    Active
    Completed
)

func (e Status) String() string {
    switch e {
    case Pending:
        return "Pending"
    case Active:
        return "Active"
    case Completed:
        return "Completed"
    default:
        return "Unknown"
    }
}

Ve şu girdi verildiğinde:

Priority
2
Low
High

Çıktınız şu olmalıdır:

// Code generated by enum generator. DO NOT EDIT.

const (
    Low Priority = iota
    High
)

func (e Priority) String() string {
    switch e {
    case Low:
        return "Low"
    case High:
        return "High"
    default:
        return "Unknown"
    }
}

Bu alıştırma, stringer gibi araçların arkasındaki temel kavramı göstermektedir; çalışma zamanında yansıma (reflection) kullanmak yerine derleme zamanında tekrarlayan ancak tür açısından güvenli (type-safe) kod üretmek. Oluşturucunuz, bir dosyaya kaydedilebilecek ve projenin geri kalanıyla birlikte derlenebilecek geçerli Go kodu üretir.

Kopya kağıdı

Kod üretimi, çalışma zamanında türleri incelemek yerine derleme zamanında Go kaynak kodu oluşturarak, boilerplate kod miktarını azaltırken tip güvenliği ve daha iyi performans sağlar.

go generate komutu, özel yorum yönergelerini kullanarak kod üretimini otomatikleştirir:

//go:generate stringer -type=Status

type Status int

const (
    Pending Status = iota
    Active
    Completed
)

go generate ./... komutunu çalıştırmak, belirtilen aracı (stringer gibi) çağırır; bu araç, programınızla birlikte derlenen ve üretilmiş kodları içeren yeni bir dosya oluşturur.

Yaygın kod üretme araçları şunlardır:

  • stringer - enum türleri için String() metotları üretir
  • mockgen - arayüzlerden test mock'ları oluşturur
  • protoc - Protocol Buffer tanımlarından kod üretir

Reflection'a (yansıma) göre temel avantajları:

  • Üretilen kod derleyici tarafından kontrol edilir
  • Hatalar çalışma zamanında değil, derleme zamanında yakalanır
  • Daha iyi performans

Dezavantajları arasında fazladan bir derleme adımı ve deponuzda yönetilmesi gereken üretilmiş dosyalar yer alır.

Kendin dene

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Enum tür adını oku
	typeName, _ := reader.ReadString('\n')
	typeName = strings.TrimSpace(typeName)

	// Değer sayısını oku
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))

	// Her bir değer adını oku
	values := make([]string, count)
	for i := 0; i < count; i++ {
		value, _ := reader.ReadString('\n')
		values[i] = strings.TrimSpace(value)
	}

	// TODO: Okunan verilerle bir EnumType oluşturun

	// TODO: GenerateFullCode kullanarak tam kodu oluşturun ve yazdırı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