Tasarım kalıpları, yazılım tasarımındaki yaygın sorunlara yönelik tekrarlayan çözümlerdir. Gang of Four (GoF), bunları üç ana tipe ayırır: Oluşturucu (Creational), Yapısal (Structural) ve Davranışsal (Behavioral). Bu yazıda, Go kullanarak gerçek dünya uygulamalarını, netlik, basitlik ve diline özgü (idiomatic) kodlara odaklanarak inceliyoruz. İster mikro hizmetler ister karmaşık monolitler geliştiriyor olun, bu kalıpları anlamak sürdürülebilir, ölçeklenebilir ve test edilebilir kod yazmanıza yardımcı olur.
Oluşturucu Kalıplar: Nesne Oluşturma
Oluşturucu kalıplar, nesne oluşturma mekanizmalarıyla ilgilenir ve nesneleri duruma uygun bir şekilde oluşturmaya çalışır. En yaygın olanı Factory Method (Fabrika Metodu)'dur.
Go'da Factory Method
Go'da arayüz tabanlı fabrika metotları diline özgüdür (idiomatic). Birden fazla işlemciyi destekleyen bir ödeme sistemini düşünün:
type PaymentProcessor interface {
Process(amount float64) error
}
type StripeProcessor struct{}
func (s StripeProcessor) Process(amount float64) error {
fmt.Printf("Processing $%.2f via Stripe\n", amount)
return nil
}
type PayPalProcessor struct{}
func (p PayPalProcessor) Process(amount float64) error {
fmt.Printf("Processing $%.2f via PayPal\n", amount)
return nil
}
func NewPaymentProcessor(provider string) PaymentProcessor {
switch provider {
case "stripe":
return StripeProcessor{}
case "paypal":
return PayPalProcessor{}
default:
return nil
}
}
Bu, istemci kodunu somut uygulamalardan ayırır. Mevcut kodu değiştirmeden yeni ödeme yöntemlerini kolayca ekleyebilir, Açık/Kapalı İlkesi'ne (Open/Closed Principle) uyum sağlayabilirsiniz.
Yapısal Kalıplar: Nesne Bileşimi
Yapısal kalıplar, sınıfların ve nesnelerin daha büyük yapılar oluşturmak için nasıl bir araya getirildiğiyle ilgilenir. Adapter (Uyarlayıcı) Kalıbı, uyumsuz arayüzlerin entegrasyonu için kritiktir.
Go'da Adapter Pattern
Modern uygulamanızın kayıt tutucusundan (logger) farklı olan bir eski (legacy) kayıt arayüzünüz olduğunu varsayalım:
type LegacyLogger interface {
Log(msg string)
}
type AppLogger interface {
Info(msg string)
Error(msg string)
}
type LoggerAdapter struct {
legacy LegacyLogger
}
func (a LoggerAdapter) Info(msg string) {
a.legacy.Log("[INFO] " + msg)
}
func (a LoggerAdapter) Error(msg string) {
a.legacy.Log("[ERROR] " + msg)
}
func NewLoggerAdapter(legacy LegacyLogger) AppLogger {
return LoggerAdapter{legacy: legacy}
}
Bu uyarlayıcı, modern uygulamanızın arayüzünü değiştirmeden eski kayıt tutucusunu kullanmasını sağlar; bu da kod yeniden kullanımını ve kademeli yeniden yapılandırmayı (refactoring) teşvik eder.
Davranışsal Kalıplar: Nesne Etkileşimi
Davranışsal kalıplar, algoritmalarla ve nesneler arası sorumluluk dağılımıyla ilgilenir. Observer (Gözlemci) Kalıbı, olay tabanlı mimariler için hayati önem taşır.
Go'da Observer Pattern
Go'nun kanalları (channels), asenkron iletişim için Observer kalıbının doğal bir uygulamasını sunar:
type Event struct {
ID int
Data string
}
type EventHandler func(Event)
type EventBus struct {
handlers map[string][]EventHandler
}
func NewEventBus() *EventBus {
return &EventBus{handlers: make(map[string][]EventHandler)}
}
func (eb *EventBus) Subscribe(eventType string, handler EventHandler) {
eb.handlers[eventType] = append(eb.handlers[eventType], handler)
}
func (eb *EventBus) Publish(event Event) {
for _, handler := range eb.handlers["order"] {
go handler(event)
}
func main() {
bus := NewEventBus()
bus.Subscribe("order", func(e Event) {
fmt.Printf("Email sent for order %d\n", e.ID)
})
bus.Subscribe("order", func(e Event) {
fmt.Printf("Notification sent for order %d\n", e.ID)
})
bus.Publish(Event{ID: 1, Data: "New Order"})
}
Bu gevşek bağlama (loose coupling), birden çok abonenin birbirinden habersiz olarak olaylara tepki vermesine olanak tanır; bu da mikro hizmetler ve olay tabanlı sistemler için idealdir.
Sonuç
Tasarım kalıpları, her duruma uyan tek bir çözüm değil, yaygın mimari zorlukları ele alan kanıtlanmış şablonlardır. Oluşturucu kalıplar nesne oluşturmayı basitleştirir, yapısal kalıplar nesneler arası ilişkileri yönetir ve davranışsal kalıplar iletişimi ele alır. Go'da, bu kalıpları diline özgü bir şekilde uygulamak için arayüzler ve kanallar gibi dilin güçlü yönlerinden yararlanın. Unutmayın: Spesifik sorununuza çözüm getiren kalıpları seçin ve uymadıkları yerlere zorlamayın. Bu kalıpları ustalaşmak, kodunuzu işlevselden sağlam ve sürdürülebilir bir seviyeye taşıyacaktır.