Menu
Coddy logo textTech

Dependency Injection

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

Dependency Injection, bir struct'ın bağımlılıklarını dahili olarak oluşturmak yerine dışarıdan aldığı bir tekniktir. Go'da, interfaces (arayüzler) bu deseni doğal ve güçlü kılar.

Bir struct içine belirli bir uygulamayı doğrudan yazmak (hardcoding) yerine, bir arayüz (interface) 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 struct oluşturun:

type OrderService struct {
    notifier Notifier  // bağımlılık arayüz üzerinden enjekte edildi
}

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 veya umursamaz. Servisi oluştururken bağımlılığı enjekte edersiniz:

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 hale getirir. Test sırasında, aslında mesaj göndermeyen sahte (mock) bir bildirimci (notifier) enjekte edebilirsiniz. Üretim ortamında gerçek uygulamayı enjekte edersiniz. Her iki senaryoda da struct değişmeden kalır.

challenge icon

Görev

Kolay

Bağımlılık enjeksiyonunun (dependency injection) gücünü gösteren bir günlükleme (logging) sistemi oluşturalım. Günlükleri farklı hedeflere yazabilen bir servis oluşturacaksınız; üstelik bu servis, günlüklerin gerçekte nereye gittiğini bilmek veya umursamak zorunda kalmayacak.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • logger.go: Bir Log(message string) string metodu gerektiren bir Logger arayüzü (interface) tanımlayın. Ardından iki farklı günlükçü (logger) uygulaması oluşturun:
    • Prefix alanına sahip ConsoleLoggerLog metodu [CONSOLE] [Prefix]: [message] değerini döndürür.
    • Filename alanına sahip FileLoggerLog metodu [FILE:[Filename]] [message] değerini döndürür.
  • service.go: Logger arayüzüne (somut bir türe değil) bağımlı olan bir AppService yapısı (struct) oluşturun. Bir Logger kabul eden ve AppService'e bir işaretçi (pointer) döndüren NewAppService adlı bir yapıcı (constructor) fonksiyon ekleyin. AppService'e, enjekte edilen günlükçüyü kullanarak Processing: [task] mesajını günlükleyen ve günlükçüden dönen sonucu döndüren DoWork(task string) string adlı bir metot verin.
  • main.go: Yapılandırmayı girdiden okuyun, her iki günlükçü türünü de oluşturun, her birini ayrı AppService örneklerine enjekte edin ve sağlanan görevle her serviste DoWork metodunu çağırın. Her servis çağrısından gelen sonucu yazdırın.

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

  • Satır 1: Konsol günlükçü öneki (prefix)
  • Satır 2: Dosya günlükçü dosya adı (filename)
  • Satır 3: İşlenecek görev (task)

Örneğin, INFO, app.log ve user authentication verildiğinde, çıktınız şu şekilde olmalıdır:

[CONSOLE] INFO: Processing: user authentication
[FILE:app.log] Processing: user authentication

AppService'in bir konsola mı yoksa bir dosyaya mı günlükleme yaptığını nasıl bilmediğine dikkat edin; sadece enjekte edilen günlükçü neyse onun üzerindeki Log metodunu çağırır. Bu esneklik, bağımlılık enjeksiyonunun özüdür: aynı servis kodu, tamamen farklı günlükleme uygulamalarıyla çalışır.

Kopya kağıdı

Dependency Injection (Bağımlılık Enjeksiyonu), bir struct'ın bağımlılıklarını dahili olarak oluşturmak yerine dışarıdan aldığı bir tekniktir. Go'da arayüzler (interfaces) bu deseni doğal ve güçlü kılar.

Bağımlılık için bir arayüz tanımlayın:

type Notifier interface {
    Send(message string) string
}

Arayüzün somut uygulamalarını oluşturun:

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
}

Somut bir tipe değil, arayüze bağlı olan bir struct oluşturun:

type OrderService struct {
    notifier Notifier  // bağımlılık arayüz aracılığıyla enjekte edildi
}

func NewOrderService(n Notifier) *OrderService {
    return &OrderService{notifier: n}
}

func (o *OrderService) PlaceOrder(item string) string {
    return o.notifier.Send("Order placed: " + item)
}

Servisi oluştururken bağımlılığı enjekte edin:

emailService := NewOrderService(EmailNotifier{})
fmt.Println(emailService.PlaceOrder("Book"))  // Email: Order placed: Book

smsService := NewOrderService(SMSNotifier{})
fmt.Println(smsService.PlaceOrder("Phone"))   // SMS: Order placed: Phone

Bu yaklaşım, struct'ın kodunu değiştirmeden farklı uygulamaların değiştirilmesine olanak tanıyarak kodu daha esnek ve test edilebilir hale getirir.

Kendin dene

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Konsol günlükçü önekini oku
	prefix, _ := reader.ReadString('\n')
	prefix = prefix[:len(prefix)-1]
	
	// Dosya günlükçü 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: Önek ile bir ConsoleLogger oluşturun
	
	// TODO: Dosya adı ile bir FileLogger oluşturun
	
	// TODO: ConsoleLogger enjekte edilmiş bir AppService oluşturun
	
	// TODO: FileLogger enjekte edilmiş başka bir AppService oluşturun
	
	// TODO: Her bir serviste görev ile DoWork metodunu çağırın ve sonuçları yazdırın
	fmt.Println("result1")
	fmt.Println("result2")
}
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