Singleton — pola konstruktif yang menjamin instance tunggal kelas dan menyediakan titik akses global ke sana. Singleton banyak digunakan dalam pengembangan mobile untuk sumber daya bersama: klien jaringan, basis data, manajer pengaturan. Pola ini dijelaskan dalam buku klasik GoF (1994) dan tetap menjadi salah satu yang paling dikenal. Selengkapnya — di Refactoring Guru: Singleton.
Poin Penting
Singleton — pola desain konstruktif yang dijelaskan oleh GoF (Gang of Four) pada tahun 1994. Pola ini memecahkan dua tugas: membatasi pembuatan instance kelas menjadi satu objek dan menyediakan akses global ke objek tersebut. Singleton berguna untuk sumber daya yang harus unik: pabrik sesi, cache gambar, manajer koneksi basis data, klien Crashlytics atau Analytics.
Implementasi Singleton memerlukan konstruktor privat (melarang pembuatan eksternal), bidang statis dengan instance tunggal, dan metode akses statis (shared, instance, getInstance). Klien memanggil Singleton.shared.method() tanpa khawatir tentang pembuatan objek. Pola ini populer di iOS dan Android: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — semuanya adalah Singleton. Namun penggunaan Singleton yang berlebihan mengarah ke anti-pola Global State.
Masalah Singleton — dependensi tersembunyi (kelas secara implisit bergantung pada objek Singleton), kesulitan pengujian (tidak dapat mengganti instance dalam pengujian tanpa upaya tambahan), pelanggaran Single Responsibility Principle (Singleton mengelola instance-nya sendiri dan logika bisnis). Pengembangan mobile modern lebih memilih DI (Dagger, Hilt, Swinject) untuk mengelola instance tunggal — kontainer DI membuat objek sekali dan menyuntikkannya melalui konstruktor.
Swift Singleton diimplementasikan melalui properti statis shared dengan inisialisasi privat. Sejak Swift 3, inisialisasi malas properti statis dijamin thread-safe — kompiler secara otomatis menambahkan sinkronisasi melalui dispatch_once. Cukup mendeklarasikan static let shared = Class() dan membuat init() privat. Swift tidak memerlukan sinkronisasi tambahan untuk akses single-thread setelah inisialisasi.
final class NetworkManager {
// Thread-safe Singleton
static let shared = NetworkManager()
private init() {
URLSessionConfiguration.default.timeoutIntervalForRequest = 30
}
private var cache = NSCache<NSString, NSData>()
func fetchData(from url: URL) async throws -> Data {
let key = url.absoluteString as NSString
if let cached = cache.object(forKey: key) {
return cached as Data
}
let (data, _) = try await URLSession.shared.data(from: url)
cache.setObject(data as NSData, forKey: key)
return data
}
}
// Penggunaan
let data = try await NetworkManager.shared.fetchData(from: url)
Apple Singleton — di iOS SDK banyak objek menggunakan Singleton: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard. Apple menggunakan Singleton untuk layanan yang secara fisik unik (satu layar, satu aplikasi). Pengembang menyalin pola ini untuk layanan mereka. Di SwiftUI akses global ke Singleton digantikan oleh Environment dan @EnvironmentObject, yang meningkatkan testabilitas.
Kotlin Singleton — cara paling sederhana: kata kunci object mendeklarasikan kelas-singleton dengan inisialisasi malas pada akses pertama. Kotlin object thread-safe dan tidak memerlukan sinkronisasi tambahan. Jika Singleton dengan parameter konstruktor diperlukan, companion object dengan delegasi lazy digunakan. Di Android Singleton sering diperlukan untuk konteks Application dan layanan yang diinisialisasi melalui Application.onCreate().
// Opsi 1: object — Singleton sederhana tanpa parameter
object AppPreferences {
private val prefs = Application.instance
.getSharedPreferences("app", Context.MODE_PRIVATE)
var isFirstLaunch: Boolean
get() = prefs.getBoolean("first_launch", true)
set(value) = prefs.edit { putBoolean("first_launch", value) }
}
// Opsi 2: companion object — Singleton dengan parameter
class ApiClient private constructor(baseUrl: String) {
companion object {
@Volatile
private var instance: ApiClient? = null
fun getInstance(baseUrl: String): ApiClient {
return instance ?: this.synchronized {
instance ?: ApiClient(baseUrl).also { instance = it }
}
}
}
fun request(endpoint: String): String { /* ... */ }
}
Android SDK Singleton — banyak layanan sistem Android mengimplementasikan Singleton: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder(). Contohnya termasuk SharedPreferences, MediaPlayer, AudioManager. Di aplikasi Android Singleton sering digunakan untuk repositori, manajer, dan pabrik. Google merekomendasikan mengganti Singleton dengan DI (Hilt, Koin), di mana lingkup Singleton (Scope.Singleton atau @Singleton) dikelola oleh kontainer, dan kelas tetap testable.
Thread safety — persyaratan kritis untuk Singleton di lingkungan multi-thread. Tanpa sinkronisasi, dua thread dapat secara bersamaan memeriksa instance == null dan membuat dua instance. Solusinya — penguncian saat pembuatan pertama dan pelepasan setelah inisialisasi. Di Swift properti statis (static let) secara default thread-safe. Di Kotlin object thread-safe. Untuk gaya Java di Kotlin digunakan synchronized atau @Volatile + double-check locking.
| Bahasa | Mekanisme | Thread safety | Inisialisasi malas |
|---|---|---|---|
| Swift | static let | dispatch_once (otomatis) | Ya, pada akses pertama |
| Kotlin object | Object declaration | Inisialisator kelas thread-safe | Ya, pada akses pertama |
| Kotlin companion | synchronized + @Volatile | Double-checked locking | Ya, melalui lazy atau synchronized |
| Java | synchronized + volatile | Double-checked locking | Ya, di getInstance() |
Double-checked locking — pola untuk inisialisasi malas Singleton. Pemeriksaan pertama tanpa sinkronisasi (cepat, jika instance sudah dibuat), kedua — di dalam synchronized (pembuatan hanya oleh satu thread). @Volatile menjamin visibilitas perubahan untuk semua thread. Tanpa volatile thread lain mungkin melihat objek yang dibuat sebagian. Di Kotlin delegasi lazy dengan LazyThreadSafetyMode.SYNCHRONIZED secara otomatis mengimplementasikan double-checked locking.
Dependency Injection — alternatif Singleton untuk mengelola instance tunggal. Kontainer DI (Dagger, Hilt, Koin, Swinject) membuat objek sekali dalam lingkup Singleton dan menyuntikkannya melalui konstruktor. Kelas tidak tahu tentang status Singleton-nya — ini diputuskan oleh kontainer. Kode menjadi testable: dalam pengujian modul DI diganti dengan modul mock. Kelebihan DI: dependensi eksplisit di konstruktor, kemungkinan penggantian, siklus hidup terpadu.
Kapan Singleton dibenarkan — objek tingkat sistem: Crashlytics, Analytics, Logging. Layanan ini diinisialisasi sekali di AppDelegate/Application dan digunakan di mana-mana. DI berlebihan untuk mereka. Singleton juga nyaman untuk cache gambar (NSCache, Coil, Glide), di mana akses global dibenarkan oleh kinerja. Untuk yang lainnya DI lebih disukai: membuat dependensi terlihat, menyederhanakan pengujian dan refactoring.
Pendekatan hibrida — Singleton dengan kemampuan penggantian untuk pengujian. Di Swift digunakan protokol + properti statis yang dapat diganti oleh pengujian (misalnya melalui URLProtocol untuk URLSession). Di Kotlin — kelas terbuka dengan properti injectable, di mana pengujian mengatur mock melalui refleksi atau setter. Pendekatan ini mempertahankan kesederhanaan Singleton, tetapi memberikan alat untuk pengujian. Google merekomendasikan Hilt untuk Android, Apple tidak memaksakan DI untuk iOS — pilihan tergantung pada tim.
Pertanyaan yang Sering Diajukan
Tidak, Singleton adalah pola GoF, tetapi penggunaannya yang sering salah mengubahnya menjadi anti-pola Global State. Singleton dibenarkan untuk sumber daya yang secara fisik unik (layar, printer, sistem file). Masalah muncul ketika Singleton digunakan untuk mengelola data: dependensi tersembunyi, kesulitan pengujian, pelanggaran Single Responsibility Principle. Alternatif modern — DI dengan lingkup Singleton.
Tiga pendekatan: (1) melalui protokol — Singleton mengimplementasikan protokol, pengujian mengganti implementasi; (2) melalui DI — Singleton disuntikkan sebagai dependensi melalui konstruktor; (3) melalui metode reset — Singleton memiliki metode untuk mereset status dalam pengujian (hanya untuk build pengujian). Pendekatan pertama lebih disukai, ketiga — berbahaya untuk production. Swift memungkinkan mengganti properti shared melalui manipulasi runtime dalam pengujian.
Kotlin object — konstruksi bahasa yang membuat Singleton di tingkat bytecode. Berbeda dengan implementasi Java dengan konstruktor privat dan getInstance(), object menjamin thread safety, inisialisasi malas, dan larangan pewarisan. Java Singleton memerlukan sinkronisasi manual (synchronized) dan volatile untuk kerja yang benar di lingkungan multi-thread. Kotlin object — cara teraman dan paling ringkas di Android.
Pewarisan Singleton melanggar pola: jika kelas Singleton dapat diwariskan, subkelas dapat membuat instance kedua, melanggar keunikan. Di Swift final class melarang pewarisan. Kotlin object tidak dapat diwariskan (object — sealed). Jika Singleton dengan variasi diperlukan, gunakan kontainer DI dengan lingkup Singleton: menjamin satu instance dan mendukung pewarisan melalui antarmuka.
Parameter diteruskan melalui init(context: Application) atau getInstance(param). Kotlin object tidak menerima parameter — gunakan companion object dengan metode pabrik getInstance(param). Hilt memecahkan masalah: @Singleton + @Inject constructor(context: Application) — kontainer DI menyuntikkan konteks Application secara otomatis. Untuk klien retrofit parameter (baseUrl, interceptors) diteruskan melalui builder di modul DI.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga