Menu
Coddy logo textTech

Singleton Kalıbı

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

Singleton deseni, bir türün programınız boyunca yalnızca bir örneğe sahip olmasını sağlar ve ona küresel bir erişim noktası sunar. Bu, yapılandırma yöneticileri, veritabanı bağlantıları veya günlükçüler gibi paylaşılan kaynaklar için kullanışlıdır.

Go'da, iş parçacığı açısından güvenli (thread-safe) başlatmayı garanti etmek için sync.Once ile birleştirilmiş paket düzeyinde bir değişken kullanarak Singleton'ı uygularız:

package config

import "sync"

type Config struct {
    DatabaseURL string
    MaxRetries  int
}

var (
    instance *Config
    once     sync.Once
)

func GetInstance() *Config {
    once.Do(func() {
        instance = &Config{
            DatabaseURL: "localhost:5432",
            MaxRetries:  3,
        }
    })
    return instance
}

sync.Once, birden fazla goroutine GetInstance() fonksiyonunu eşzamanlı olarak çağırsa bile, başlatma fonksiyonunun tam olarak bir kez çalışmasını garanti eder. Sonraki her çağrı, başlatma kodunu yeniden yürütmeden aynı örneği döndürür.

cfg1 := config.GetInstance()
cfg2 := config.GetInstance()
// cfg1 ve cfg2 aynı örneği işaret eder

Singleton'ı idareli kullanın. Kullanışlı olsa da, testi zorlaştırabilen ve bağımlılıkları gizleyebilen global bir durum (global state) sunar. Kullanım durumunuz için bağımlılık enjeksiyonunun (dependency injection) daha iyi bir alternatif olup olmayacağını değerlendirin.

challenge icon

Görev

Kolay

Uygulamanız boyunca yalnızca bir logger örneğinin var olmasını sağlayan bir Logger singleton oluşturalım! Bu, Singleton deseninin klasik bir kullanım örneğidir; programınızın tüm bölümlerinin aynı logger yapılandırmasını ve durumunu paylaşmasını istersiniz.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • logger.go: İş parçacığı güvenli (thread-safe) Singleton logger'ınızı uygulayın.

    İki alana sahip bir Logger yapısı (struct) oluşturun: günlük mesajı önekleri için Prefix (string) ve kaç mesajın günlüğe kaydedildiğini takip etmek için MessageCount (int).

    Yalnızca bir örneğin oluşturulduğundan emin olmak için sync.Once ile paket düzeyinde değişkenler kullanın. Logger'ı varsayılan "[LOG]" önekiyle başlatan ve singleton örneğini döndüren bir GetLogger() fonksiyonu uygulayın.

    Logger'ınıza şu metotları ekleyin:

    • SetPrefix(prefix string) - logger'ın önekini değiştirir
    • Log(message string) string - mesaj sayısını artırır ve biçimlendirilmiş bir dize döndürür: [prefix] #[count]: [message]
    • GetCount() int - günlüğe kaydedilen toplam mesaj sayısını döndürür
  • main.go: GetLogger() fonksiyonuna yapılan birden fazla çağrının aynı örneği döndürdüğünü gösterin.

    Yeni bir önek değeri okuyun, ardından bir mesaj sayısı ve ardından günlüğe kaydedilecek her bir mesajı okuyun.

    Logger örneğini alın, özel öneki ayarlayın, ardından her mesajı günlüğe kaydedin ve sonucu yazdırın. Tüm mesajları günlüğe kaydettikten sonra, logger örneğini tekrar alın (aynı örnek olduğunu kanıtlamak için) ve toplam mesaj sayısını yazdırın.

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

  • Satır 1: Ayarlanacak özel önek
  • Satır 2: Mesaj sayısı
  • Sonraki satırlar: Günlüğe kaydedilecek her bir mesaj

Örneğin, şu girdi verildiğinde:

[APP]
3
Server started
User connected
Request processed

Çıktınız şu olmalıdır:

[APP] #1: Server started
[APP] #2: User connected
[APP] #3: Request processed
Total messages logged: 3

Ve şu girdi verildiğinde:

[DEBUG]
2
Initializing cache
Cache ready

Çıktınız şu olmalıdır:

[DEBUG] #1: Initializing cache
[DEBUG] #2: Cache ready
Total messages logged: 2

Ve şu girdi verildiğinde:

[ERROR]
1
Connection failed

Çıktınız şu olmalıdır:

[ERROR] #1: Connection failed
Total messages logged: 1

Buradaki temel nokta, GetLogger() fonksiyonunu kaç kez çağırırsanız çağırın, her zaman paylaşılan durumuyla birlikte aynı örneği almanızdır. Mesaj sayısı kalıcıdır çünkü var olan tek bir Logger vardır!

Kopya kağıdı

Singleton deseni, bir türün programınız boyunca yalnızca bir örneğe sahip olmasını sağlar ve bu örneğe küresel bir erişim noktası sunar. Bu, yapılandırma yöneticileri, veritabanı bağlantıları veya günlükçüler (loggers) gibi paylaşılan kaynaklar için kullanışlıdır.

İş parçacığı güvenli (thread-safe) bir başlatma için sync.Once ile birleştirilmiş paket düzeyinde bir değişken kullanarak Singleton'ı uygulayın:

package config

import "sync"

type Config struct {
    DatabaseURL string
    MaxRetries  int
}

var (
    instance *Config
    once     sync.Once
)

func GetInstance() *Config {
    once.Do(func() {
        instance = &Config{
            DatabaseURL: "localhost:5432",
            MaxRetries:  3,
        }
    })
    return instance
}

sync.Once, birden fazla goroutine aynı anda GetInstance() fonksiyonunu çağırsa bile, başlatma fonksiyonunun tam olarak bir kez çalışmasını garanti eder. Sonraki her çağrı aynı örneği döndürür:

cfg1 := config.GetInstance()
cfg2 := config.GetInstance()
// cfg1 and cfg2 point to the same instance

Singleton'ı idareli kullanın; çünkü test etmeyi zorlaştırabilen ve bağımlılıkları gizleyebilen küresel bir durum (global state) sunar. Alternatif olarak bağımlılık enjeksiyonunu (dependency injection) değerlendirin.

Kendin dene

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Özel öneki oku
	prefix, _ := reader.ReadString('\n')
	prefix = strings.TrimSpace(prefix)

	// Mesaj sayısını oku
	countStr, _ := reader.ReadString('\n')
	countStr = strings.TrimSpace(countStr)
	count, _ := strconv.Atoi(countStr)

	// Her bir mesajı oku
	messages := make([]string, count)
	for i := 0; i < count; i++ {
		msg, _ := reader.ReadString('\n')
		messages[i] = strings.TrimSpace(msg)
	}

	// TODO: GetLogger() kullanarak logger örneğini al

	// TODO: SetPrefix() kullanarak özel öneki ayarla

	// TODO: Her mesajı günlüğe kaydet ve sonucu yazdır

	// TODO: Logger örneğini tekrar al (aynısı olduğunu kanıtlamak için)
	// ve toplam mesaj sayısını şu formatta yazdır: "Total messages logged: X"
}
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