Singleton (təkliq) — sinifin yeganə nümunəsini təmin edən və ona qlobal giriş nöqtəsi təqdim edən yaradıcı nümunə. Singleton mobil inkişafda paylaşılan resurslar üçün geniş tətbiq olunur: şəbəkə müştəriləri, verilənlər bazaları, tənzimləmə menecerləri. Nümunə klassik GoF (1994) kitabında təsvir edilmişdir və ən tanınanlardan biri olaraq qalır. Ətraflı — Refactoring Guru: Singleton.
Əsas
Singleton (təkliq) — 1994-cü ildə GoF (Gang of Four) tərəfindən təsvir edilən yaradıcı dizayn nümunəsi. Nümunə iki məsələni həll edir: sinif nümunəsinin yaradılmasını bir obyektlə məhdudlaşdırır və həmin obyektə qlobal giriş təmin edir. Singleton unikal olmalı resurslar üçün faydalıdır: sessiya fabriki, şəkil keşi, verilənlər bazası qoşulma meneceri, Crashlytics və ya Analytics müştərisi.
Singleton tətbiqi özəl konstruktor (xarici yaradılmanı qadağan edir), yeganə nümunə ilə statik sahə və statik giriş metodu (shared, instance, getInstance) tələb edir. Müştərilər obyektin yaradılması ilə məşğul olmadan Singleton.shared.method() çağırır. Nümunə iOS və Android-də populyardır: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — bunların hamısı Singleton-dur. Lakin Singleton-un həddindən artıq istifadəsi Global State anti-nümunəsinə gətirib çıxarır.
Singleton problemləri — gizli asılılıqlar (siniflər Singleton obyektindən örtülü asılıdır), test etmənin çətinliyi (əlavə səy olmadan testdə nümunəni əvəz etmək mümkün deyil), Single Responsibility Principle-un pozulması (Singleton həm öz nümunəsini, həm də biznes məntiqini idarə edir). Müasir mobil inkişaf tək nümunələri idarə etmək üçün DI (Dagger, Hilt, Swinject) üstünlük verir — DI konteyneri obyekti bir dəfə yaradır və onu konstruktor vasitəsilə əlavə edir.
Swift Singleton özəl başlatıcı ilə shared statik xüsusiyyəti vasitəsilə tətbiq edilir. Swift 3-dən etibarən, statik xüsusiyyətlərin ləng başladılması təminatlı olaraq iplik təhlükəsizdir — kompilyator dispatch_once vasitəsilə avtomatik sinxronizasiya əlavə edir. static let shared = Class() elan etmək və init()-i özəl etmək kifayətdir. Swift başlatmadan sonra tək iplikli giriş üçün əlavə sinxronizasiya tələb etmir.
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
}
}
// İstifadə
let data = try await NetworkManager.shared.fetchData(from: url)
Apple Singleton — iOS SDK-da bir çox obyekt Singleton istifadə edir: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard. Apple fiziki olaraq unikal olan xidmətlər üçün Singleton istifadə edir (bir ekran, bir tətbiq). Tərtibatçılar bu nümunəni öz xidmətləri üçün kopyalayırlar. SwiftUI-də Singleton-a qlobal giriş Environment və @EnvironmentObject ilə əvəz olunur, bu da test edilə bilənliyi yaxşılaşdırır.
Kotlin Singleton — ən sadə yol: object açar sözü ilk girişdə ləng başlatma ilə sinif-singleton elan edir. Kotlin object iplik təhlükəsizdir və əlavə sinxronizasiya tələb etmir. Konstruktor parametrləri olan Singleton lazımdırsa, lazy-delegat ilə companion object istifadə olunur. Android-də Singleton tez-tez Application konteksti və Application.onCreate() vasitəsilə başladılan xidmətlər üçün lazımdır.
// Variant 1: object — parametrsiz sadə Singleton
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) }
}
// Variant 2: companion object — parametrlərlə Singleton
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 — Android-in bir çox sistem xidmətləri Singleton tətbiq edir: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder(). Nümunələrə SharedPreferences, MediaPlayer, AudioManager daxildir. Android tətbiqlərində Singleton tez-tez depolar, menecerlər və fabriklər üçün istifadə olunur. Google Singleton-u DI (Hilt, Koin) ilə əvəz etməyi tövsiyə edir, burada Singleton əhatəsi (Scope.Singleton və ya @Singleton) konteyner tərəfindən idarə olunur və sinif test edilə bilən qalır.
Thread safety — çoxiplikli mühitdə Singleton üçün kritik tələb. Sinxronizasiya olmadan iki iplik eyni anda instance == null yoxlayıb iki nümunə yarada bilər. Həlli — ilk yaratmada bloklama və başlatmadan sonra sərbəst buraxma. Swift-də statik xüsusiyyətlər (static let) susmaya görə iplik təhlükəsizdir. Kotlin-də object iplik təhlükəsizdir. Kotlin-də Java üslubu üçün synchronized və ya @Volatile + double-check locking istifadə olunur.
| Dil | Mexanizm | İplik təhlükəsizliyi | Ləng başlatma |
|---|---|---|---|
| Swift | static let | dispatch_once (avtomatik) | Bəli, ilk girişdə |
| Kotlin object | Object declaration | Sinif başlatıcısı iplik təhlükəsizdir | Bəli, ilk girişdə |
| Kotlin companion | synchronized + @Volatile | Double-checked locking | Bəli, lazy və ya synchronized vasitəsilə |
| Java | synchronized + volatile | Double-checked locking | Bəli, getInstance() daxilində |
Double-checked locking — Singleton-un ləng başladılması üçün nümunə. Birinci yoxlama sinxronizasiya olmadan (nümunə artıq yaradılıbsa sürətli), ikincisi — synchronized daxilində (yalnız bir iplik tərəfindən yaratma). @Volatile bütün ipliklər üçün dəyişikliklərin görünməsini təmin edir. Volatile olmadan başqa bir iplik qismən yaradılmış obyekti görə bilər. Kotlin-də LazyThreadSafetyMode.SYNCHRONIZED ilə lazy-delegat avtomatik olaraq double-checked locking tətbiq edir.
Dependency Injection — tək nümunəni idarə etmək üçün Singleton-a alternativ. DI konteyneri (Dagger, Hilt, Koin, Swinject) obyekti Singleton əhatəsində bir dəfə yaradır və onu konstruktor vasitəsilə əlavə edir. Sinif öz Singleton statusu haqqında bilmir — bu konteyner tərəfindən qərar verilir. Kod test edilə bilən olur: testdə DI modulu mock modulu ilə əvəz olunur. DI-nin üstünlükləri: konstruktorda aşkar asılılıqlar, ləğv etmə imkanı, vahid həyat dövrü.
Singleton nə vaxt əsaslandırılır — sistem səviyyəli obyektlər: Crashlytics, Analytics, Logging. Bu xidmətlər AppDelegate/Application-da bir dəfə başladılır və hər yerdə istifadə olunur. DI onlar üçün həddindən artıqdır. Singleton həmçinin şəkil keşləri (NSCache, Coil, Glide) üçün əlverişlidir, burada qlobal giriş performansla əsaslandırılır. Qalan hər şey üçün DI üstünlük təşkil edir: asılılıqları görünən edir, test etmə və yenidənqurmanı sadələşdirir.
Hibrid yanaşma — testlər üçün ləğv etmə imkanı olan Singleton. Swift-də protokol + statik xüsusiyyət istifadə olunur, test onu əvəz edə bilər (məsələn, URLSession üçün URLProtocol vasitəsilə). Kotlin-də — refleksiya və ya setter vasitəsilə testin mock təyin etdiyi injectable xüsusiyyəti olan open class. Bu yanaşma Singleton-un sadəliyini qoruyur, lakin test üçün vasitələr verir. Google Android üçün Hilt tövsiyə edir, Apple iOS üçün DI tələb etmir — seçim komandadan asılıdır.
Tez-tez verilən suallar
Xeyr, Singleton GoF nümunəsidir, lakin onun tez-tez səhv istifadəsi onu Global State anti-nümunəsinə çevirir. Singleton fiziki olaraq unikal resurslar (ekran, printer, fayl sistemi) üçün əsaslandırılır. Problemlər Singleton məlumatların idarə edilməsi üçün istifadə edildikdə yaranır: gizli asılılıqlar, test etmənin çətinliyi, Single Responsibility Principle-un pozulması. Müasir alternativ — Singleton əhatəsi ilə DI.
Üç yanaşma: (1) protokol vasitəsilə — Singleton protokolu tətbiq edir, testlər tətbiqi əvəz edir; (2) DI vasitəsilə — Singleton konstruktor vasitəsilə asılılıq olaraq əlavə edilir; (3) reset-metod vasitəsilə — Singleton testlərdə vəziyyəti sıfırlamaq üçün metoda malikdir (yalnız test buildi üçün). Birinci yanaşma üstünlük təşkil edir, üçüncüsü — istehsal üçün təhlükəlidir. Swift testlərdə runtime manipulyasiyaları vasitəsilə shared xüsusiyyətini əvəz etməyə imkan verir.
Kotlin object — bayt kod səviyyəsində Singleton yaradan dil konstruksiyası. Java tətbiqindən fərqli olaraq özəl konstruktor və getInstance() ilə, object iplik təhlükəsizliyi, ləng başlatma və miras alma qadağasını təmin edir. Java Singleton çoxiplikli mühitdə düzgün iş üçün əl ilə sinxronizasiya (synchronized) və volatile tələb edir. Kotlin object — Android-də ən təhlükəsiz və yığcam yoldur.
Singleton-u miras almaq nümunəni pozur: əgər Singleton sinfi miras alına bilirsə, alt sinif ikinci nümunə yarada bilər, unikallığı pozaraq. Swift-də final class miras almanı qadağan edir. Kotlin object miras alına bilməz (object — sealed). Variantlı Singleton lazımdırsa, Singleton əhatəsi ilə DI konteynerindən istifadə edin: bir nümunəni təmin edir və interfeyslər vasitəsilə miras almanı dəstəkləyir.
Parametrlər init(context: Application) və ya getInstance(param) vasitəsilə ötürülür. Kotlin object parametr qəbul etmir — fabrik metodu getInstance(param) ilə companion object istifadə edin. Hilt problemi həll edir: @Singleton + @Inject constructor(context: Application) — DI konteyneri Application kontekstini avtomatik əlavə edir. Retrofit müştərisi üçün parametrlər (baseUrl, interceptors) DI modulunda builder vasitəsilə ötürülür.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun