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.

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.

challenge icon

Görev

Kolay

Bağı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: Logger metodunu gerektiren bir Log(message string) string arayüzü tanımla. Ardından iki farklı günlükçü uygulaması oluştur:
    • ConsoleLogger, Prefix alanıyla birlikte. Log metodu [CONSOLE] [Prefix]: [message] değerini döndürür
    • FileLogger, Filename alanıyla birlikte. Log metodu [FILE:[Filename]] [message] değerini döndürür
  • service.go: Somut bir türe değil, AppService arayüzüne bağlı olan bir Logger yapısı oluştur. Bir NewAppService kabul eden ve Logger işaretçisi döndüren AppService oluşturucu işlevini ekle. AppService yapısına, enjekte edilen günlükçüyü kullanarak DoWork(task string) string mesajını günlüğe kaydeden ve günlükçünün döndürdüğü değeri döndüren Processing: [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 serviste DoWork metodunu ç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 authentication

AppService'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")
}
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

Kendi başına pratik yap: Online Go derleyicisi