Singleton

All lectures for KK purposes
Деңгей , Сабақ
Қол жетімді

Көпшілігіңіз Singleton-ды жақсы виски деп естіген боларсыз, бірақ алкоголь денсаулығымызға зиян болғандықтан, бүгін Java-дағы Singleton паттерн жайлы айтып береміз.

Бұрыннан объектілер жасаумен таныспыз және Java объектісін жасау үшін мынаны жазу керек деп білетінбіз:


Robot robot = new Robot(); 
    

Бірақ, егер маған тек бір ғана класс экземпляры керек болса ше?

new Robot() пайдалана отырып, көп объектілер жасауға болады, және мұны ешкім бізге тыйым сала алмайды. Осындай жағдайларда көмекке Singleton келеді.

Мысалы: жалғыз ғана принтерге қосылатын және оған басу тапсырмасын беретін қосымша жазу керек делік:


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

Кәдімгі класс сияқты ғой, бірақ! Оның бір ғана "бірақ" бар: мен бірнеше экземпляр жасай аламын және оларды әртүрлі жерлерден шақыра аламын. Бұл принтеріме жаман әсер етуі немесе тіпті оны бұзуы мүмкін. Сондықтан принтерімізді тек бір экземплярда жасауымыз керек, және бұл жерде Singleton көмекке келеді!

Singleton жасау тәсілдері

Singleton жасаудың екі тәсілі бар:

  • жабық конструктор пайдалану;
  • жалғыз экземплярға қол жеткізу үшін ашық статикалық әдіс экспорттау.

Алдымен жабық конструктор пайдаланатын тәсілді қарастырайық. Ол үшін кластағы өрісті final ретінде жариялап, оны инициализациялауымыз керек. Ол бізде final болғандықтан, ол Immutable болады, және оны өзгерте алмаймыз.

Сонымен қатар, конструкторды private деп жариялауымыз керек, бұл класс сыртынан объект жасауға тыйым салады. Бұл біздің принтеріміздің жүйеде тек бір экземпляры болатынына кепілдік береді. Конструктор инициализация кезінде бір рет шақырылып, бізге Printer жасайды:


public class Printer { 
     
	public static final Printer PRINTER = new Printer(); 
     
	private Printer() { 
	} 
 
	public void print() { 
        //Басып шығару.... 
 
	} 
}
    

Жабық конструктор пайдаланып бір ғана экземпляр болатын PRINTER синглтон жасадық. PRINTER айнымалысы static модификаторына ие, өйткені ол объектке емес, Printer класына тиісті болады.

Енді жалғыз экземплярға қол жеткізу үшін статикалық әдіспен singleton жасауды қарастырайық (ескеріңіз, өріс private болды):


public class Printer { 
 
	private static final Printer PRINTER = new Printer(); 
 
	private Printer() { 
	} 
 
	public static Printer getInstance() { 
    	return PRINTER; 
	} 
     
	public void print() { 
        //Басып шығару.... 
	} 
} 
    

getInstance() әдісін қанша рет шақырсақ та, әрқашан бір ғана объект экземплярын аламыз, PRINTER.

private конструкторымен singleton жасау – бұл, біріншіден, қарапайым және қысқа нұсқа, екіншіден, API айқын болады, өйткені public деп жарияланған өріс final, бұл оның әрдайым бір объект сілтемесін ұстайтынына кепілдік береді.

Статикалық әдісті пайдалану арқылы singleton-ды классқа өзгерте аламыз, ол сондай болмауы мүмкін, оның API-ін өзгертпей-ақ. getInstance() әдісі объектіміздің жалғыз экземплярын береді, бірақ оны әр пайдаланушы үшін бөлек экземпляр қайтару үшін жасауға болады.

Сондай-ақ, статикалық әдіс нұсқасын пайдаланып, жалпылама singleton фабрикасын жазуға болады.

Статикалық әдістің тағы бір артықшылығы - оны метод рeference арқылы пайдалануға мүмкіндік береді.

Егер жоғарыда аталған артықшылықтардың ешқайсысы қажет болмаса, онда public өрісті нұсқасын пайдалануды ұсынамыз.

Егер бізге сериализация қажет болса, оны орындау үшін жай ғана Serializable интерфейсін жүзеге асыру жеткіліксіз: десериализация кезінде жаңа singleton экземплярын алмас үшін readResolve әдісін қосу керек.

Сериализация объект күйін байт тәртібінде сақтау үшін қажет, ал десериализация – байттардан объектіні қалпына келтіру үшін керек. Сериализация және десериализация туралы толығырақ мына мақаладан оқи аласыз.

Енді біздің singleton-ды қайта жазамыз:


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

Мұнда оның сериализациясын және десериализациясын жасаймыз.

Төмендегі мысал Java-дағы стандартты сериализация және десериализация механизмін көрсетеді. Кодта не болып жатқанын толық түсіну Java Syntax модуліндегі "Кіріс-шығыс ағындары" және Java Core модуліндегі "Сериализация" тақырыптарын оқығаннан кейін келеді.

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);
    

Және нәтиже аламыз:

Singleton 1 is: Printer@6be46e8f
Singleton 2 is: Printer@3c756e4d

Мұнда біз десериализация кезінде біздің singleton-ның басқа класын алғанымызды көреміз. Мұны түзету үшін класымызға readResolve әдісін қосамыз:


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; 
	} 
}
    

Енді біздің singleton-ды тағы бір рет сериализациялаймыз және десериализациялаймыз:


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); 
    

Және аламыз:

Singleton 1 is: com.company.Printer@6be46e8f
Singleton 2 is: com.company.Printer@6be46e8f

readResolve() әдісі бізге десериализацияланған объектіні ғана алуға мүмкіндік береді, сол арқылы жалған singleton-дар жасауды болдырмауға болады.

Қорытындылар

Сонымен, бүгін біз singleton туралы білдік: оны қалай жасау және қашан пайдалану, ол не үшін қажет және Java-да оны жасаудың қандай нұсқалары бар. Төменде екі нұсқаның ерекшеліктері келтірілген:

Жабық конструктор Статикалық әдіс
  • Қарапайым және қысқа нұсқа
  • API айқын, өйткені өріс public final
  • Метод рeference пайдалану
  • Жалпыға ортақ singleton фабрикасын жазу мүмкіндігі
  • Әр пайдаланушы үшін бөлек экземпляр қайтаратынын жасау мүмкіндігі
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION