Singleton (सिंगलटन) — एक क्रिएशनल पैटर्न जो क्लास का एकमात्र इंस्टेंस सुनिश्चित करता है और उस तक वैश्विक पहुँच प्रदान करता है। Singleton मोबाइल डेवलपमेंट में साझा संसाधनों के लिए व्यापक रूप से उपयोग किया जाता है: नेटवर्क क्लाइंट, डेटाबेस, सेटिंग्स मैनेजर। यह पैटर्न GoF (1994) की क्लासिक पुस्तक में वर्णित है और सबसे पहचाने जाने वाले पैटर्न में से एक बना हुआ है। अधिक जानकारी Refactoring Guru: Singleton पर।
मुख्य बिंदु
Singleton — GoF (Gang of Four) द्वारा 1994 में वर्णित एक क्रिएशनल डिज़ाइन पैटर्न। यह पैटर्न दो समस्याएँ हल करता है: यह क्लास के इंस्टेंसिएशन को एक ऑब्जेक्ट तक सीमित करता है और उस ऑब्जेक्ट तक वैश्विक पहुँच प्रदान करता है। Singleton उन संसाधनों के लिए उपयोगी है जो अद्वितीय होने चाहिए: सेशन फ़ैक्टरी, इमेज कैश, डेटाबेस कनेक्शन मैनेजर, Crashlytics या Analytics क्लाइंट।
Singleton कार्यान्वयन में प्राइवेट कंस्ट्रक्टर (बाहरी निर्माण को रोकता है), एकमात्र इंस्टेंस वाला स्थैतिक फ़ील्ड और स्थैतिक एक्सेस विधि (shared, instance, getInstance) की आवश्यकता होती है। क्लाइंट ऑब्जेक्ट निर्माण की चिंता किए बिना Singleton.shared.method() कॉल करते हैं। यह पैटर्न iOS और Android में लोकप्रिय है: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — ये सभी Singleton हैं। हालाँकि, Singleton का अत्यधिक उपयोग Global State एंटी-पैटर्न की ओर ले जाता है।
Singleton समस्याएँ — छिपी निर्भरताएँ (क्लासेस अंतर्निहित रूप से Singleton ऑब्जेक्ट पर निर्भर होती हैं), परीक्षण जटिलता (अतिरिक्त प्रयास के बिना परीक्षणों में इंस्टेंस को बदला नहीं जा सकता), Single Responsibility Principle का उल्लंघन (Singleton अपने इंस्टेंस और व्यावसायिक तर्क दोनों का प्रबंधन करता है)। आधुनिक मोबाइल डेवलपमेंट एकल इंस्टेंस के प्रबंधन के लिए DI (Dagger, Hilt, Swinject) पसंद करता है — DI कंटेनर ऑब्जेक्ट को एक बार बनाता है और कंस्ट्रक्टर के माध्यम से इंजेक्ट करता है।
Swift Singleton प्राइवेट इनिशियलाइज़र के साथ स्थैतिक shared गुण के माध्यम से कार्यान्वित किया जाता है। Swift 3 के बाद से, स्थैतिक गुणों की लेज़ी इनिशियलाइज़ेशन थ्रेड-सेफ़ होने की गारंटी है — कंपाइलर स्वचालित रूप से dispatch_once के माध्यम से सिंक्रनाइज़ेशन जोड़ता है। static let shared = Class() घोषित करना और init() को प्राइवेट बनाना पर्याप्त है। Swift को इनिशियलाइज़ेशन के बाद सिंगल-थ्रेडेड एक्सेस के लिए अतिरिक्त सिंक्रनाइज़ेशन की आवश्यकता नहीं होती।
final class NetworkManager {
// थ्रेड-सेफ़ 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
}
}
// उपयोग
let data = try await NetworkManager.shared.fetchData(from: url)
Apple Singleton — iOS SDK के कई ऑब्जेक्ट Singleton का उपयोग करते हैं: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard। Apple शारीरिक रूप से अद्वितीय सेवाओं (एक स्क्रीन, एक एप्लिकेशन) के लिए Singleton का उपयोग करता है। डेवलपर अपनी सेवाओं के लिए इस पैटर्न की नकल करते हैं। SwiftUI में, Singleton तक वैश्विक पहुँच को Environment और @EnvironmentObject से बदल दिया जाता है, जिससे परीक्षण क्षमता में सुधार होता है।
Kotlin Singleton — सबसे सरल तरीका: object कीवर्ड पहली पहुँच पर लेज़ी इनिशियलाइज़ेशन के साथ सिंगलटन क्लास घोषित करता है। Kotlin object थ्रेड-सेफ़ है और अतिरिक्त सिंक्रनाइज़ेशन की आवश्यकता नहीं है। यदि कंस्ट्रक्टर पैरामीटर के साथ Singleton की आवश्यकता है, तो lazy डेलिगेट के साथ companion object का उपयोग किया जाता है। Android में, Application कॉन्टेक्स्ट और Application.onCreate() के माध्यम से इनिशियलाइज़ होने वाली सेवाओं के लिए Singleton अक्सर आवश्यक होता है।
// विकल्प 1: object — बिना पैरामीटर के सरल 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) }
}
// विकल्प 2: companion object — पैरामीटर के साथ 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 की कई सिस्टम सेवाएँ Singleton लागू करती हैं: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder()। उदाहरणों में SharedPreferences, MediaPlayer, AudioManager शामिल हैं। Android एप्लिकेशन में, Singleton का उपयोग अक्सर रिपॉज़िटरी, मैनेजर और फ़ैक्टरी के लिए किया जाता है। Google Singleton को DI (Hilt, Koin) से बदलने की सलाह देता है, जहाँ Singleton स्कोप (Scope.Singleton या @Singleton) कंटेनर द्वारा प्रबंधित किया जाता है जबकि क्लास परीक्षण योग्य रहती है।
Thread safety — मल्टीथ्रेडेड वातावरण में Singleton के लिए एक महत्वपूर्ण आवश्यकता। सिंक्रनाइज़ेशन के बिना, दो थ्रेड एक साथ instance == null की जाँच कर सकते हैं और दो इंस्टेंस बना सकते हैं। समाधान पहले निर्माण के दौरान लॉक करना और इनिशियलाइज़ेशन के बाद जारी करना है। Swift में, स्थैतिक गुण (static let) डिफ़ॉल्ट रूप से थ्रेड-सेफ़ होते हैं। Kotlin में, object थ्रेड-सेफ़ है। Kotlin में Java-शैली के लिए, synchronized या @Volatile + double-check locking का उपयोग किया जाता है।
| भाषा | तंत्र | थ्रेड सुरक्षा | लेज़ी इनिशियलाइज़ेशन |
|---|---|---|---|
| Swift | static let | dispatch_once (स्वचालित) | हाँ, पहली पहुँच पर |
| Kotlin object | Object घोषणा | क्लास इनिशियलाइज़र थ्रेड-सेफ़ | हाँ, पहली पहुँच पर |
| Kotlin companion | synchronized + @Volatile | Double-checked locking | हाँ, lazy या synchronized के माध्यम से |
| Java | synchronized + volatile | Double-checked locking | हाँ, getInstance() में |
Double-checked locking — Singleton की लेज़ी इनिशियलाइज़ेशन के लिए एक पैटर्न। पहली जाँच बिना सिंक्रनाइज़ेशन के (तेज़ यदि इंस्टेंस पहले से मौजूद है), दूसरी synchronized के अंदर (केवल एक थ्रेड द्वारा निर्माण)। @Volatile सभी थ्रेड के लिए परिवर्तनों की दृश्यता सुनिश्चित करता है। volatile के बिना, दूसरा थ्रेड आंशिक रूप से निर्मित ऑब्जेक्ट देख सकता है। Kotlin में, LazyThreadSafetyMode.SYNCHRONIZED के साथ lazy डेलिगेट स्वचालित रूप से double-checked locking लागू करता है।
Dependency Injection — एकल इंस्टेंस के प्रबंधन के लिए Singleton का एक विकल्प। DI कंटेनर (Dagger, Hilt, Koin, Swinject) ऑब्जेक्ट को एक बार Singleton स्कोप में बनाता है और कंस्ट्रक्टर के माध्यम से इंजेक्ट करता है। क्लास को अपनी Singleton स्थिति के बारे में पता नहीं होता — कंटेनर निर्णय लेता है। कोड परीक्षण योग्य हो जाता है: परीक्षणों में DI मॉड्यूल को mock मॉड्यूल से बदल दिया जाता है। DI के लाभ: कंस्ट्रक्टर में स्पष्ट निर्भरताएँ, ओवरराइड करने की क्षमता, एकीकृत जीवनचक्र।
Singleton कब उचित है — सिस्टम-स्तरीय ऑब्जेक्ट: Crashlytics, Analytics, Logging। ये सेवाएँ AppDelegate/Application में एक बार इनिशियलाइज़ होती हैं और हर जगह उपयोग की जाती हैं। उनके लिए DI अत्यधिक है। Singleton इमेज कैश (NSCache, Coil, Glide) के लिए भी सुविधाजनक है, जहाँ वैश्विक पहुँच प्रदर्शन द्वारा उचित है। बाकी सबके लिए, DI बेहतर है: यह निर्भरताओं को दृश्यमान बनाता है, परीक्षण और रीफैक्टरिंग को सरल बनाता है।
हाइब्रिड दृष्टिकोण — परीक्षणों के लिए ओवरराइड करने की क्षमता वाला Singleton। Swift में, एक प्रोटोकॉल + स्थैतिक गुण जिसे परीक्षण बदल सकते हैं (उदाहरण के लिए, URLSession के लिए URLProtocol के माध्यम से)। Kotlin में, एक इंजेक्टेबल गुण वाली open क्लास, जहाँ परीक्षण रिफ्लेक्शन या setter के माध्यम से mock सेट करते हैं। यह दृष्टिकोण Singleton की सरलता बनाए रखता है लेकिन परीक्षण क्षमताएँ प्रदान करता है। Google Android के लिए Hilt की सलाह देता है, Apple iOS के लिए DI थोपता नहीं — चुनाव टीम पर निर्भर करता है।
अक्सर पूछे जाने वाले प्रश्न
नहीं, Singleton एक GoF पैटर्न है, लेकिन इसका बार-बार गलत उपयोग इसे Global State एंटी-पैटर्न में बदल देता है। Singleton शारीरिक रूप से अद्वितीय संसाधनों (स्क्रीन, प्रिंटर, फ़ाइल सिस्टम) के लिए उचित है। समस्याएँ तब उत्पन्न होती हैं जब Singleton का उपयोग डेटा प्रबंधन के लिए किया जाता है: छिपी निर्भरताएँ, परीक्षण जटिलता, Single Responsibility Principle का उल्लंघन। आधुनिक विकल्प Singleton स्कोप के साथ DI है।
तीन दृष्टिकोण: (1) प्रोटोकॉल के माध्यम से — Singleton एक प्रोटोकॉल लागू करता है, परीक्षण कार्यान्वयन बदल देते हैं; (2) DI के माध्यम से — Singleton को कंस्ट्रक्टर के माध्यम से निर्भरता के रूप में इंजेक्ट किया जाता है; (3) reset विधि के माध्यम से — Singleton में परीक्षणों में स्थिति रीसेट करने की विधि होती है (केवल परीक्षण बिल्ड के लिए)। पहला दृष्टिकोण बेहतर है, तीसरा प्रोडक्शन के लिए खतरनाक है। Swift परीक्षणों में रनटाइम हेरफेर के माध्यम से shared गुण को बदलने की अनुमति देता है।
Kotlin object एक भाषा निर्माण है जो बाइटकोड स्तर पर Singleton बनाता है। प्राइवेट कंस्ट्रक्टर और getInstance() वाले Java कार्यान्वयन के विपरीत, object थ्रेड सुरक्षा, लेज़ी इनिशियलाइज़ेशन की गारंटी देता है और इनहेरिटेंस को प्रतिबंधित करता है। Java Singleton को मल्टीथ्रेडेड वातावरण में सही संचालन के लिए मैन्युअल सिंक्रनाइज़ेशन (synchronized) और volatile की आवश्यकता होती है। Kotlin object Android में सबसे सुरक्षित और संक्षिप्त तरीका है।
Singleton को इनहेरिट करना पैटर्न को तोड़ता है: यदि Singleton क्लास को इनहेरिट किया जा सकता है, तो एक सबक्लास दूसरा इंस्टेंस बना सकता है, जो विशिष्टता का उल्लंघन करता है। Swift में, final class इनहेरिटेंस को प्रतिबंधित करता है। Kotlin object को इनहेरिट नहीं किया जा सकता (object sealed है)। यदि विविधता वाले Singleton की आवश्यकता है, तो Singleton स्कोप वाले DI कंटेनर का उपयोग करें: यह एकल इंस्टेंस की गारंटी देता है और इंटरफ़ेस के माध्यम से इनहेरिटेंस का समर्थन करता है।
पैरामीटर init(context: Application) या getInstance(param) के माध्यम से पास किए जाते हैं। Kotlin object पैरामीटर स्वीकार नहीं करता — फ़ैक्टरी विधि getInstance(param) के साथ companion object का उपयोग करें। Hilt समस्या हल करता है: @Singleton + @Inject constructor(context: Application) — DI कंटेनर स्वचालित रूप से Application कॉन्टेक्स्ट इंजेक्ट करता है। Retrofit क्लाइंट के लिए, पैरामीटर (baseUrl, interceptors) DI मॉड्यूल में builder के माध्यम से पास किए जाते हैं।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें