Ko'pchiligimiz Singleton haqida yaxshi viski deb eshitgan bo'lishimiz mumkin, lekin alkogol sog'liq uchun zararli bo'lgani uchun, bugun senga Java'da Singleton dizayn patter haqida aytib beramiz.
Ilgari biz ob'ekt yaratishni o'rgangan edik va Java ob'ektini yaratish uchun quyidagini yozish kerakligini bilamiz:
Robot robot = new Robot();
Lekin agar menga faqatgina bir nechta klass nusxasi kerak bo'lsa, nima qilishim kerak?
new Robot() dan foydalanib, ko'plab ob'ektlar yasash mumkin, va hech kim bizga bu narsani taqiqlamaydi. Ana shunday holatlarda Singleton yordamga keladi.
Masalan: faqat BIR printer bilan aloqa o'rnatib, unga chop etish vazifasini yuklaydigan dastur yozishingiz mumkin:
public class Printer {
public Printer() {
}
public void print() {
…
}
}
Oddiy klassga o'xshaydi, lekin! Unda bitta narsa bor: printerning bir nechta nusxasini yaratib, ularni turli joylardan chaqira olaman. Bu printerga yomon ta'sir qilishi yoki hatto uni buziqlashi mumkin. Shu sababdan, printerning faqat bitta nusxasi bo'lishini ta'minlashimiz kerak, va bunga Singleton yordam beradi!
Singleton yaratish usullari
Singleton yaratishning ikki usuli mavjud:
- yopiq konstruktorni ishlatish;
- birgina nusxaga kirish imkonini beruvchi ochiq statik metodni eksport qilish.
Avval yopiq konstruktorni qo'llash usulini ko'rib chiqamiz. Buning uchun biz klassta maydonni final sifatida e'lon qilib, uni inicializatsiya qilishimiz kerak. U bizda final bo'lganligi sababli, biz uni o'zgartira olmaymiz, bu vaqtda u O'zgarmas (Immutable) bo'ladi.
Shuningdek, konstruktorni private sifatida e'lon qilishimiz kerak, bu klassdan tashqarida ob'ekt yaratishni taqiqlaydi. Bu bizga printerning boshqa nusxasi tizimda bo'lmasligini kafolatlaydi. Konstruktor faqat bir marta chaqiriladi va bizga Printer ni yaratadi:
public class Printer {
public static final Printer PRINTER = new Printer();
private Printer() {
}
public void print() {
//Chop etilmoqda...
}
}
Biz yopiq konstruktor ishlatib, faqat bitta nusxada bo'ladigan PRINTER singletonini yaratdik. PRINTER o'zgaruvchisi static modifikatoriga ega, chunki u ob'ektga emas, Printer klasga tegishli bo'ladi.
Endi yagona nusxaga kirish imkonini beruvchi statik metod yordamida singleton yaratishni ko'rib chiqamiz (maydon private bo'lganiga e'tibor bering):
public class Printer {
private static final Printer PRINTER = new Printer();
private Printer() {
}
public static Printer getInstance() {
return PRINTER;
}
public void print() {
//Chop etilmoqda...
}
}
Qancha marta getInstance() metodini chaqirmaymiz, doimo bizning PRINTER ob'ektimizning bir xil nusxasini olamiz.
private konstruktor yordamida singleton yaratish, birinchidan, oddiy va qisqa variant, ikkinchidan, API aniq bo'lib, public maydon final sifatida e'lon qilingan bo'lib, bu har doim bir xil ob'ektga ishora qilinishini kafolatlaydi.
Statik metoddan foydalanish varianti singletonni sinfga o'zgartirishni API ni o'zgartirmasdan imkoniyat beradi. getInstance() metodi bizga ob'ektning yagona nusxasini beradi, lekin biz uni shunday qila olamizki, har bir chaqiruvchi uchun alohida nusxani qaytarsin.
Shuningdek, statik usul yordamida singletonlar uchun umumiy fabrika yozishingiz mumkin.
Statik usulning barcha afzalliklaridan foydalanishni istamasangiz, public maydonli variantni ishlatishingizni tavsiya qilamiz.
Agar bizga serializatsiya kerak bo'lsa, Serializable interfeysini amalga oshirishdan tashqari readResolve metodini qo'shish kerak, aks holda deserializatsiya qilinganida biz yangi singleton nusxasini olamiz.
| Serializatsiya ob'ektni baytlar ketma-ketligida saqlash uchun kerak, deserializatsiya esa ob'ektni baytlardan qayta tiklash. Serializatsiya va deserializatsiya haqida ko'proq o'qib olishingiz mumkin shu maqolada. |
Endi bizim singletonni qayta yozamiz:
public class Printer implements Serializable {
private static final Printer PRINTER = new Printer();
private Printer() {
}
public static Printer getInstance() {
return PRINTER;
}
}
Bu yerda uni serializatsiya va deserializatsiyaga qilamiz.
| Quyidagi misol Java'da standart serializatsiya va deserializatsiya mexanizmi. Kodda nima sodir bo'layotganini to'liq tushunish "Kiritish-chiqarish oqimi" (Java Syntax moduli) va "Serializatsiya" (Java Core moduli) mavzularini o'rganganingizdan so'ng keladi. |
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);
Va natijani olamiz:
Singleton 2 is: Printer@3c756e4d
Bu yerda deserializatsiya qilinganida biz boshqa klassimiz singletonini oldik. Buni to'g'rilash uchun klassterimizga readResolve metodini qo'shamiz:
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;
}
}
Endi singletonimizni yana bir bor serializatsiya va deserializatsiya qilamiz:
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);
Va olamiz:
Singleton 2 is: com.company.Printer@6be46e8f
readResolve() metodi biz deserializatsiya qilgan obyektni olish imkonini beradi, shu bilan yolg'on singletonlar yaratishni oldini oladi.
Xulosalar
Xo'sh, bugun biz singleton haqida o'rgandik: uni qanday yaratish va qachon foydalanishni, nimaga kerakligini va Java'da qanday yaratish variantlari mavjudligini. Quyida har ikkala variantning xususiyatlari bor:
| Private konstruktor | Statik metod |
|---|---|
|
|
GO TO FULL VERSION