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.
Görev
KolayEnum 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) veValues(sabit adlarını temsil eden string dilimi) içeren, enum benzeri bir tür tanımını temsil eden birEnumTypeyapısı (struct) oluşturun.Bir
String()metodu için Go kaynak kodu üreten birGenerateStringMethod(e EnumType) stringfonksiyonu 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
iotakullanarak sabit bildirimlerini üreten birGenerateConstants(e EnumType) stringfonksiyonu 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) stringfonksiyonunu 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çinString()metotları üretirmockgen- arayüzlerden test mock'ları oluştururprotoc- 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
}
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
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool12İleri Düzey OOP Kavramları
Functional Options DeseniGo'da Builder DeseniMetot ZincirlemeTür Takma Adları vs. TanımlamalarReflection TemelleriKod Üretimine Genel Bakış