Pola Singleton
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GO di Coddy. Pelajaran 86 dari 107.
Pola Singleton memastikan bahwa suatu tipe hanya memiliki satu instance di seluruh programmu dan menyediakan titik akses global ke instance tersebut. Pola ini berguna untuk sumber daya bersama seperti pengelola konfigurasi, koneksi database, atau pencatat log.
Di Go, kita menerapkan Singleton menggunakan variabel tingkat package yang dipadukan dengan sync.Once untuk menjamin inisialisasi yang aman untuk thread:
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 menjamin bahwa fungsi inisialisasi dijalankan tepat satu kali, bahkan ketika beberapa goroutine memanggil GetInstance() secara bersamaan. Setiap pemanggilan berikutnya mengembalikan instance yang sama tanpa menjalankan ulang kode inisialisasi.
cfg1 := config.GetInstance()
cfg2 := config.GetInstance()
// cfg1 dan cfg2 merujuk ke instance yang samaGunakan Singleton secukupnya. Meskipun praktis, Singleton memperkenalkan state global yang dapat mempersulit pengujian dan menyembunyikan dependensi. Pertimbangkan apakah dependency injection bisa menjadi alternatif yang lebih baik untuk kasus penggunaanmu.
Tantangan
MudahMari buat singleton Logger yang memastikan hanya ada satu instance logger di seluruh aplikasi kamu! Ini adalah kasus penggunaan klasik untuk pola Singleton. Kamu ingin semua bagian programmu menggunakan konfigurasi dan status logger yang sama.
Kamu akan membagi kodemu ke dalam dua file:
logger.go: Implementasikan logger Singleton yang aman untuk thread.Buat struct
Loggerdengan dua field:Prefix(string) untuk awalan pesan log, danMessageCount(int) untuk melacak jumlah pesan yang sudah dicatat.Gunakan variabel tingkat package dengan
sync.Onceuntuk memastikan hanya satu instance yang dibuat. Implementasikan fungsiGetLogger()yang menginisialisasi logger dengan awalan default"[LOG]"dan mengembalikan instance singleton.Tambahkan method berikut ke Logger:
SetPrefix(prefix string)- mengubah awalan loggerLog(message string) string- menambah jumlah pesan dan mengembalikan string berformat:[prefix] #[count]: [message]GetCount() int- mengembalikan jumlah total pesan yang sudah dicatat
main.go: Tunjukkan bahwa beberapa pemanggilanGetLogger()mengembalikan instance yang sama.Baca nilai awalan baru, lalu baca jumlah pesan, diikuti dengan setiap pesan yang akan dicatat.
Dapatkan instance logger, tetapkan awalan khusus, lalu catat setiap pesan dan cetak hasilnya. Setelah mencatat semua pesan, dapatkan instance logger lagi (untuk membuktikan bahwa itu instance yang sama) dan cetak jumlah total pesan.
Input berikut akan diberikan:
- Baris 1: Awalan khusus yang akan ditetapkan
- Baris 2: Jumlah pesan
- Baris berikutnya: Setiap pesan yang akan dicatat
Contohnya, jika inputnya:
[APP]
3
Server started
User connected
Request processedOutput kamu seharusnya:
[APP] #1: Server started
[APP] #2: User connected
[APP] #3: Request processed
Total messages logged: 3Dan jika inputnya:
[DEBUG]
2
Initializing cache
Cache readyOutput kamu seharusnya:
[DEBUG] #1: Initializing cache
[DEBUG] #2: Cache ready
Total messages logged: 2Dan jika inputnya:
[ERROR]
1
Connection failedOutput kamu seharusnya:
[ERROR] #1: Connection failed
Total messages logged: 1Inti pentingnya adalah, berapa kali pun kamu memanggil GetLogger(), kamu selalu mendapatkan instance yang sama dengan status bersama. Jumlah pesan tetap tersimpan karena hanya ada satu Logger!
Coba sendiri
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Baca prefiks khusus
prefix, _ := reader.ReadString('\n')
prefix = strings.TrimSpace(prefix)
// Baca jumlah pesan
countStr, _ := reader.ReadString('\n')
countStr = strings.TrimSpace(countStr)
count, _ := strconv.Atoi(countStr)
// Baca setiap pesan
messages := make([]string, count)
for i := 0; i < count; i++ {
msg, _ := reader.ReadString('\n')
messages[i] = strings.TrimSpace(msg)
}
// TODO: Dapatkan instance logger menggunakan GetLogger()
// TODO: Atur prefiks khusus menggunakan SetPrefix()
// TODO: Catat setiap pesan dan cetak hasilnya
// TODO: Dapatkan kembali instance logger (untuk membuktikan bahwa instance-nya sama)
// dan cetak jumlah total pesan dengan format: "Total messages logged: X"
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1Dasar-Dasar OOP di Go
File EksternalWorkspace Go dan modulePaket dan ImporNama yang Diekspor dan Tidak DieksporPengantar OOP di Gostruct sebagai classMendefinisikan method pada structPointer receiver vs value receiverInisialisasi structFungsi KonstruktorUlasan: Kalkulator Sederhana4Interface
Pengenalan InterfaceImplementasi ImplisitInterface sebagai KontrakInterface Kosong (any)Type AssertionType SwitchKomposisi InterfaceInterface Stringer & ErrorUlasan: Kalkulator Bentuk7Enkapsulasi
Field yang Diekspor vs Tidak DieksporEnkapsulasi Tingkat PackageMethod Getter dan SetterPenyembunyian Informasi di GoRingkasan: Data Siswa10Generics (Go 1.18+)
Pengantar GenericsParameter TipeBatasan TipeStruct GenerikCara Alternatif untuk Method GenerikRingkasan Koleksi Generik13Pola Desain Bagian 1
Pengantar Pola DesainPola SingletonPola FactoryPola Abstract FactoryPola ObserverPola Strategy2Mendalami Tipe dan Struct
Tipe Dasar dan KompositDefinisi Tipe KustomTag StructStruct AnonimStruct BersarangNilai Nol dan Nilai BawaanRangkuman: Buku Kontak5Komposisi daripada Pewarisan
Mengapa Go Tidak Memiliki PewarisanDasar Penyematan StructPromosi methodMenyematkan Beberapa StructPenyematan vs AgregasiMembayangi method yang DisematkanRangkuman: Hierarki Karyawan8Penanganan Error & OOP
Interface errorTipe Error KustomPembungkusan Error (fmt.Errorf)Error Sentinelerrors.Is() dan errors.As()Panic, Defer, dan RecoverRangkuman: Parser File3Pointer dan Memori
Dasar-Dasar Pointer di GoPointer ke StructPass by Value vs ReferenceFungsi new()Garbage Collection di GoRekap: Pembuat Linked List6Polimorfisme di Go
Polimorfisme melalui InterfaceDuck Typing di GoAturan Pemenuhan InterfaceKoleksi PolimorfikInjeksi DependensiRangkuman: Pemroses Pembayaran9Konkruensi & OOP
Dasar-dasar GoroutineChannel & KomunikasiChannel Ber-buffer vs Tanpa BufferPernyataan selectsync.Mutex & sync.RWMutexsync.WaitGroupDesain Struct yang Aman untuk ThreadRingkasan: Worker PoolLatihan sendiri: Compiler Go online