Dependency Injection
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 45 / 107.
Bağımlılık Enjeksiyonu, bir yapının bağımlılıklarını kendi içinde oluşturmak yerine dışarıdan aldığı bir tekniktir. Go'da arayüzler bu kalıbı doğal ve güçlü hâle getirir.
Bir struct içinde belirli bir uygulamayı sabit kodlamak yerine bir arayüz kabul edersiniz. Bu, struct'ın kodunu değiştirmeden uygulamaları değiştirmenize olanak tanır:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
type SMSNotifier struct{}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
Şimdi somut bir türe değil, arayüze bağlı olan bir yapı oluşturun:
type OrderService struct {
notifier Notifier // arayüz aracılığıyla enjekte edilen bağımlılık
}
func NewOrderService(n Notifier) *OrderService {
return &OrderService{notifier: n}
}
func (o *OrderService) PlaceOrder(item string) string {
return o.notifier.Send("Order placed: " + item)
}
OrderService, e-posta mı yoksa SMS mi kullandığını bilmez ve bunu önemsemez. Hizmeti oluştururken bağımlılığı enjekte edersin:
func main() {
emailService := NewOrderService(EmailNotifier{})
fmt.Println(emailService.PlaceOrder("Book")) // Email: Sipariş verildi: Book
smsService := NewOrderService(SMSNotifier{})
fmt.Println(smsService.PlaceOrder("Phone")) // SMS: Sipariş verildi: Phone
}
Bu yaklaşım kodunuzu daha esnek ve test edilebilir hâle getirir. Test sırasında gerçekten mesaj göndermeyen bir taklit bildirimci enjekte edebilirsiniz. Üretimde ise gerçek uygulamayı enjekte edersiniz. Struct her iki senaryoda da değişmeden kalır.
Görev
KolayBağımlılık enjeksiyonunun gücünü gösteren bir günlük kaydı sistemi oluşturalım. Günlükleri farklı hedeflere yazabilen, bu günlüklerin gerçekte nereye gittiğini bilmesi veya önemsemesi gerekmeyen bir servis oluşturacaksın.
Kodunu üç dosyada düzenleyeceksin:
logger.go:Loggermetodunu gerektiren birLog(message string) stringarayüzü tanımla. Ardından iki farklı günlükçü uygulaması oluştur:ConsoleLogger,Prefixalanıyla birlikte.Logmetodu[CONSOLE] [Prefix]: [message]değerini döndürürFileLogger,Filenamealanıyla birlikte.Logmetodu[FILE:[Filename]] [message]değerini döndürür
service.go: Somut bir türe değil,AppServicearayüzüne bağlı olan birLoggeryapısı oluştur. BirNewAppServicekabul eden veLoggerişaretçisi döndürenAppServiceoluşturucu işlevini ekle.AppServiceyapısına, enjekte edilen günlükçüyü kullanarakDoWork(task string) stringmesajını günlüğe kaydeden ve günlükçünün döndürdüğü değeri döndürenProcessing: [task]adlı bir metot ekle.main.go: Yapılandırmayı girdiden oku, her iki günlükçü türünü oluştur, her birini ayrıAppServiceörneğine enjekte et ve sağlanan görevle her servisteDoWorkmetodunu çağır. Her servis çağrısının sonucunu yazdır.
Aşağıdaki girdiler sağlanacaktır:
- 1. satır: Konsol günlükçüsü öneki
- 2. satır: Dosya günlükçüsü dosya adı
- 3. satır: İşlenecek görev
Örneğin INFO, app.log ve user authentication verildiğinde çıktın şöyle olmalıdır:
[CONSOLE] INFO: Processing: user authentication
[FILE:app.log] Processing: user authenticationAppService'in konsola mı yoksa dosyaya mı günlük kaydı yaptığını bilmediğine dikkat et. Yalnızca enjekte edilen günlükçü üzerinde Log çağrısını yapar. Bu esneklik, bağımlılık enjeksiyonunun özüdür: aynı servis kodu tamamen farklı günlük kaydı uygulamalarıyla çalışır.
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Konsol logger önekini oku
prefix, _ := reader.ReadString('\n')
prefix = prefix[:len(prefix)-1]
// Dosya logger dosya adını oku
filename, _ := reader.ReadString('\n')
filename = filename[:len(filename)-1]
// İşlenecek görevi oku
task, _ := reader.ReadString('\n')
if len(task) > 0 && task[len(task)-1] == '\n' {
task = task[:len(task)-1]
}
// TODO: prefix ile bir ConsoleLogger oluştur
// TODO: filename ile bir FileLogger oluştur
// TODO: ConsoleLogger enjekte edilmiş bir AppService oluştur
// TODO: FileLogger enjekte edilmiş başka bir AppService oluştur
// TODO: Her serviste DoWork'ü task ile çağır ve sonuçları yazdır
fmt.Println("result1")
fmt.Println("result2")
}
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 PoolKendi başına pratik yap: Online Go derleyicisi