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 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 gibi paylaşılan kaynaklar için kullanışlıdır.

Go'da, iş parçacığı güvenli başlatmayı garanti etmek için Singleton'ı paket düzeyinde bir değişkeni sync.Once ile birleştirerek 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 aynı anda GetInstance() çağırsa bile başlatma işlevinin tam olarak bir kez çalıştırılmasını garanti eder. Sonraki her çağrı, başlatma kodunu yeniden çalıştırmadan aynı örneği döndürür.

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

Singleton'ı tasarruflu kullanın. Kullanışlı olsa da test etmeyi zorlaştırabilecek ve bağımlılıkları gizleyebilecek küresel durum ortaya çıkarır. Kullanım alanınız için bağımlılık eklemenin daha iyi bir alternatif olup olamayacağı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 alanıdır. Programınızın tüm bölümlerinin aynı logger yapılandırmasını ve durumunu paylaşmasını istersiniz.

Kodunuzu iki dosyaya ayıracaksınız:

  • logger.go: İş parçacığı güvenli Singleton logger'ı uygulayın.

    İki alana sahip bir Logger 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şturulmasını sağlamak için sync.Once ile paket düzeyinde değişkenler kullanın. Logger'ı varsayılan GetLogger() önekiyle başlatan ve singleton örneğini döndüren bir "[LOG]" işlevi uygulayın.

    Logger'ınıza şu yöntemleri 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() işlevine yapılan birden çok çağrının aynı örneği döndürdüğünü gösterin.

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

    Logger örneğini alın, özel öneki ayarlayın, ardından her mesajı günlüğe kaydedip 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:

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

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

[APP]
3
Server started
User connected
Request processed

Çıktınız şöyle olmalıdır:

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

Ve şu giriş verildiğinde:

[DEBUG]
2
Initializing cache
Cache ready

Çıktınız şöyle olmalıdır:

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

Ve şu giriş verildiğinde:

[ERROR]
1
Connection failed

Çıktınız şöyle olmalıdır:

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

Temel fikir şudur: GetLogger() işlevini kaç kez çağırırsanız çağırın, paylaşılan durumuyla her zaman aynı örneği alırsınız. Yalnızca bir Logger var olduğu için mesaj sayısı kalıcıdır!

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 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ı kaydet ve sonucu yazdır

	// TODO: Logger örneğini tekrar al (aynı olduğunu kanıtlamak için)
	// and print the total message count in format: "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

Kendi başına pratik yap: Online Go derleyicisi