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 ederSingleton'ı 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.
Görev
KolayUygulamanı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
Loggerstruct'ı oluşturun: günlük mesajı önekleri içinPrefix(string) ve kaç mesajın günlüğe kaydedildiğini takip etmek içinMessageCount(int).Yalnızca bir örneğin oluşturulmasını sağlamak için
sync.Onceile paket düzeyinde değişkenler kullanın. Logger'ı varsayılanGetLogger()ö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ştirirLog(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: 3Ve ş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: 2Ve şu giriş verildiğinde:
[ERROR]
1
Connection failedÇıktınız şöyle olmalıdır:
[ERROR] #1: Connection failed
Total messages logged: 1Temel 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"
}
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