शायद आपने सुना है कि सिंगलटन सिंगल माल्ट स्कॉच व्हिस्की अच्छी है? खैर, शराब आपकी सेहत के लिए खराब है, इसलिए आज हम आपको इसके बजाय जावा में सिंगलटन डिजाइन पैटर्न के बारे में बताएंगे।

हमने पहले वस्तुओं के निर्माण की समीक्षा की थी, इसलिए हम जानते हैं कि जावा में एक वस्तु बनाने के लिए, आपको कुछ इस तरह लिखना होगा:


Robot robot = new Robot(); 
    

लेकिन क्या होगा अगर हम यह सुनिश्चित करना चाहते हैं कि कक्षा का केवल एक ही उदाहरण बनाया जाए?

नया रोबोट () स्टेटमेंट कई ऑब्जेक्ट बना सकता है, और कुछ भी हमें ऐसा करने से नहीं रोकता है। यहीं पर सिंगलटन पैटर्न बचाव के लिए आता है।

मान लीजिए आपको एक एप्लिकेशन लिखने की आवश्यकता है जो एक प्रिंटर से कनेक्ट होगा - केवल एक प्रिंटर - और इसे प्रिंट करने के लिए कहें:


public class Printer { 
 
	public Printer() { 
	} 
     
	public void print() { 
    	… 
	} 
}
    

यह एक साधारण वर्ग जैसा दिखता है... लेकिन! एक "लेकिन" है: मैं अपने प्रिंटर ऑब्जेक्ट के कई उदाहरण बना सकता हूं और विभिन्न स्थानों पर कॉल विधियों को बना सकता हूं। इससे मेरा प्रिंटर खराब हो सकता है या टूट भी सकता है। इसलिए हमें यह सुनिश्चित करने की आवश्यकता है कि हमारे प्रिंटर का केवल एक उदाहरण है, और एक सिंगलटन हमारे लिए यही करेगा!

सिंगलटन बनाने के तरीके

सिंगलटन बनाने के दो तरीके हैं:

  • एक निजी कन्स्ट्रक्टर का प्रयोग करें;
  • एक उदाहरण तक पहुँच प्रदान करने के लिए एक सार्वजनिक स्थैतिक विधि निर्यात करें।

आइए पहले एक निजी कंस्ट्रक्टर का उपयोग करने पर विचार करें। ऐसा करने के लिए, हमें अपनी कक्षा में एक क्षेत्र को अंतिम रूप से घोषित करने और इसे आरंभ करने की आवश्यकता है। चूंकि हमने इसे अंतिम के रूप में चिह्नित किया है, हम जानते हैं कि यह अपरिवर्तनीय होगा , अर्थात अब हम इसे बदल नहीं सकते।

कक्षा के बाहर वस्तुओं को बनाने से रोकने के लिए आपको कन्स्ट्रक्टर को निजी घोषित करने की भी आवश्यकता है । यह हमारे लिए गारंटी देता है कि कार्यक्रम में हमारे प्रिंटर का कोई अन्य उदाहरण नहीं होगा। आरंभीकरण के दौरान कंस्ट्रक्टर को केवल एक बार कॉल किया जाएगा और हमारा प्रिंटर बनाएगा :


public class Printer { 
     
	public static final Printer PRINTER = new Printer(); 
     
	private Printer() { 
	} 
 
	public void print() { 
        // Printing... 
 
	} 
}
    

हमने PRINTER सिंगलटन बनाने के लिए एक निजी कंस्ट्रक्टर का उपयोग किया - केवल एक ही उदाहरण होगा। मुद्रकचर में स्थिर संशोधक होता है , क्योंकि यह किसी वस्तु का नहीं, बल्कि स्वयं प्रिंटर वर्ग का होता है।

अब आइए अपनी कक्षा के एक उदाहरण तक पहुँच प्रदान करने के लिए एक स्थिर विधि का उपयोग करके एक सिंगलटन बनाने पर विचार करें (और ध्यान दें कि फ़ील्ड अब निजी है ):


public class Printer { 
 
	private static final Printer PRINTER = new Printer(); 
 
	private Printer() { 
	} 
 
	public static Printer getInstance() { 
    	return PRINTER; 
	} 
     
	public void print() { 
        // Printing... 
	} 
} 
    

कोई फर्क नहीं पड़ता कि हम यहां कितनी बार getInstance () मेथड को कॉल करते हैं, हमें हमेशा वही मिलेगामुद्रकवस्तु।

निजी कंस्ट्रक्टर का उपयोग करके सिंगलटन बनाना सरल और अधिक संक्षिप्त है। क्या अधिक है, एपीआई स्पष्ट है, क्योंकि सार्वजनिक क्षेत्र को अंतिम घोषित किया गया है , जो गारंटी देता है कि इसमें हमेशा एक ही वस्तु का संदर्भ होगा।

स्टैटिक मेथड ऑप्शन हमें बिना एपीआई बदले सिंगलटन को नॉन-सिंगलटन क्लास में बदलने की सुविधा देता है। GetInstance () विधि हमें अपनी वस्तु का एक उदाहरण देती है, लेकिन हम इसे बदल सकते हैं ताकि यह कॉल करने वाले प्रत्येक उपयोगकर्ता के लिए एक अलग उदाहरण लौटाए।

स्थैतिक विकल्प हमें एक सामान्य सिंगलटन फैक्ट्री लिखने की सुविधा भी देता है।

स्थिर विकल्प का अंतिम लाभ यह है कि आप इसे विधि संदर्भ के साथ उपयोग कर सकते हैं।

यदि आपको उपरोक्त लाभों में से किसी की आवश्यकता नहीं है, तो हम उस विकल्प का उपयोग करने की अनुशंसा करते हैं जिसमें एक सार्वजनिक क्षेत्र शामिल है।

यदि हमें क्रमांकन की आवश्यकता है, तो यह केवल Serializable इंटरफ़ेस को लागू करने के लिए पर्याप्त नहीं होगा । हमें रीडरिज़ॉल्व विधि को भी जोड़ने की आवश्यकता है, अन्यथा हमें अक्रमांकन के दौरान एक नया सिंगलटन उदाहरण मिलेगा।

किसी वस्तु की स्थिति को बाइट्स के अनुक्रम के रूप में सहेजने के लिए सीरियलाइज़ेशन की आवश्यकता होती है, और उन बाइट्स से ऑब्जेक्ट को पुनर्स्थापित करने के लिए डीरियलाइज़ेशन की आवश्यकता होती है। आप इस लेख में क्रमांकन और अक्रमांकन के बारे में अधिक पढ़ सकते हैं ।

अब हम अपने सिंगलटन को फिर से लिखते हैं:


public class Printer implements Serializable { 
 
	private static final Printer PRINTER = new Printer(); 
 
	private Printer() { 
	} 
 
	public static Printer getInstance() { 
    	return PRINTER; 
	} 
} 
    

अब हम इसे क्रमानुसार और deserialize करेंगे।

ध्यान दें कि नीचे दिया गया उदाहरण जावा में क्रमांकन और अक्रमांकन के लिए मानक तंत्र है। कोड में क्या हो रहा है इसकी पूरी समझ "आई/ओ स्ट्रीम" (जावा सिंटैक्स मॉड्यूल में) और "सीरियलाइजेशन" (जावा कोर मॉड्यूल में) का अध्ययन करने के बाद आएगी।

var printer = Printer.getInstance(); 
var fileOutputStream = new FileOutputStream("printer.txt"); 
var objectOutputStream = new ObjectOutputStream(fileOutputStream); 
objectOutputStream.writeObject(printer); 
objectOutputStream.close(); 
 
var fileInputStream = new FileInputStream("printer.txt"); 
var objectInputStream = new ObjectInputStream(fileInputStream); 
var deserializedPrinter =(Printer) objectInputStream.readObject(); 
objectInputStream.close(); 
 
System.out.println("Singleton 1 is: " + printer); 
System.out.println("Singleton 2 is: " + deserializedPrinter);
    

और हमें यह परिणाम मिलता है:

सिंगलटन 1 है: प्रिंटर@6be46e8f
सिंगलटन 2 है: प्रिंटर@3c756e4d

यहाँ हम देखते हैं कि डिसेरिएलाइज़ेशन ने हमें हमारे सिंगलटन का एक अलग उदाहरण दिया। इसे ठीक करने के लिए, आइए अपनी कक्षा में रीडरिज़ॉल्व विधि जोड़ें:


public class Printer implements Serializable { 
 
	private static final Printer PRINTER = new Printer(); 
 
	private Printer() { 
	} 
 
	public static Printer getInstance() { 
    	return PRINTER; 
	} 
 
	public Object readResolve() { 
    	return PRINTER; 
	} 
}
    

अब हम अपने सिंगलटन को फिर से क्रमानुसार और डिसेरिएलाइज़ करेंगे:


var printer = Printer.getInstance(); 
var fileOutputStream = new FileOutputStream("printer.txt"); 
var objectOutputStream = new ObjectOutputStream(fileOutputStream); 
objectOutputStream.writeObject(printer); 
objectOutputStream.close(); 
 
var fileInputStream = new FileInputStream("printer.txt"); 
var objectInputStream = new ObjectInputStream(fileInputStream); 
var deserializedPrinter=(Printer) objectInputStream.readObject(); 
objectInputStream.close(); 
 
System.out.println("Singleton 1 is: " + printer); 
System.out.println("Singleton 2 is: " + deserializedPrinter); 
    

और हमें मिलता है:

सिंगलटन 1 है: com.company.Printer@6be46e8f
सिंगलटन 2 है: com.company.Printer@6be46e8f

ReadResolve () विधि हमें उसी वस्तु को प्राप्त करने की अनुमति देती है जिसे हमने डीरिएरलाइज़ किया है, जिससे दुष्ट सिंगलटन के निर्माण को रोका जा सकता है।

सारांश

आज हमने सिंगलटन के बारे में सीखा: उन्हें कैसे बनाया जाए और उनका उपयोग कब किया जाए, वे किस लिए हैं और जावा उन्हें बनाने के लिए क्या विकल्प प्रदान करता है। दोनों विकल्पों की विशिष्ट विशेषताएं नीचे दी गई हैं:

निजी निर्माणकर्ता स्थैतिक विधि
  • आसान और अधिक संक्षिप्त
  • स्पष्ट एपीआई चूंकि सिंगलटन क्षेत्र सार्वजनिक अंतिम है
  • एक विधि संदर्भ के साथ इस्तेमाल किया जा सकता है
  • एक सामान्य सिंगलटन फैक्ट्री लिखने के लिए इस्तेमाल किया जा सकता है
  • प्रत्येक उपयोगकर्ता के लिए एक अलग उदाहरण वापस करने के लिए इस्तेमाल किया जा सकता है