Singletons v Jave

1. Úvod

V tomto rýchlom článku si predstavíme dva najpopulárnejšie spôsoby implementácie Singletonov v obyčajnej Jave.

2. Singleton založený na triedach

Najpopulárnejším prístupom je implementácia Singletonu vytvorením bežnej triedy a ubezpečením, že má:

  • Súkromný konštruktér
  • Statické pole obsahujúce svoju jedinú inštanciu
  • Statická továrenská metóda na získanie inštancie

Pridáme tiež informačnú vlastnosť, ktorá slúži iba na neskoršie použitie. Naša implementácia bude teda vyzerať takto:

verejná konečná trieda ClassSingleton {súkromná statická ClassSingleton INSTANCE; private String info = "Počiatočná informačná trieda"; private ClassSingleton () {} public static ClassSingleton getInstance () {if (INSTANCE == null) {INSTANCE = new ClassSingleton (); } návrat INSTANCE; } // zakladatelia a zakladatelia}

Aj keď ide o bežný prístup, je potrebné si uvedomiť, že je to tak môže byť problematický v scenároch s viacerými vláknami, čo je hlavný dôvod používania Singletons.

Jednoducho povedané, môže to mať za následok viac ako jednu inštanciu, ktorá poruší hlavný princíp vzoru. Aj keď existujú blokovacie riešenia tohto problému, náš ďalší prístup rieši tieto problémy na koreňovej úrovni.

3. Enum Singleton

Ak sa posunieme ďalej, nebudeme diskutovať o ďalšom zaujímavom prístupe, ktorým je použitie výčtov:

public enum EnumSingleton {INSTANCE ("Informácie o počiatočnej triede"); súkromné ​​informácie o reťazci; súkromné ​​EnumSingleton (informácie o reťazci) {this.info = informácie; } public EnumSingleton getInstance () {návrat INSTANCE; } // zakladatelia a zakladatelia}

Tento prístup má serializáciu a bezpečnosť vlákien zaručenú samotnou implementáciou enum, ktorá interne zaisťuje, že je k dispozícii iba jedna inštancia, čo opravuje problémy, na ktoré upozorňuje implementácia založená na triedach.

4. Použitie

Aby sme použili našu TriedaSingleton, jednoducho musíme získať inštanciu staticky:

ClassSingleton classSingleton1 = ClassSingleton.getInstance (); System.out.println (classSingleton1.getInfo ()); // Informácie o počiatočnej triede ClassSingleton classSingleton2 = ClassSingleton.getInstance (); classSingleton2.setInfo ("Informácie o novej triede"); System.out.println (classSingleton1.getInfo ()); // Informácie o novej triede System.out.println (classSingleton2.getInfo ()); // Informácie o novej triede

Pokiaľ ide o EnumSingleton, môžeme ho použiť ako akýkoľvek iný Java Enum:

EnumSingleton enumSingleton1 = EnumSingleton.INSTANCE.getInstance (); System.out.println (enumSingleton1.getInfo ()); // Informácie o počiatočnom výčte EnumSingleton enumSingleton2 = EnumSingleton.INSTANCE.getInstance (); enumSingleton2.setInfo ("Nové informácie o enum"); System.out.println (enumSingleton1.getInfo ()); // Nové informácie o výpise System.out.println (enumSingleton2.getInfo ()); // Informácie o novom výpise

5. Časté úskalia

Singleton je klamne jednoduchý vzor návrhu a existuje niekoľko bežných chýb, ktorých by sa programátor mohol pri vytváraní singletonu dopustiť.

Rozlišujeme dva typy čísel so singletonmi:

  • existenciálne (potrebujeme jednolôžko?)
  • implementačné (implementujeme to správne?)

5.1. Existenčné problémy

Koncepčne je singleton akousi globálnou premennou. Všeobecne vieme, že sa treba vyhnúť globálnym premenným - najmä ak sú ich stavy premenlivé.

Nehovoríme, že by sme nikdy nemali používať singletony. Hovoríme však, že môžu existovať efektívnejšie spôsoby usporiadania nášho kódu.

Ak implementácia metódy závisí od jediného objektu, prečo ju neposlať ako parameter? V takom prípade výslovne ukážeme, na čom metóda závisí. V dôsledku toho môžeme tieto závislosti pri vykonávaní testov ľahko zosmiešniť (ak je to potrebné).

Napríklad singletony sa často používajú na zahrnutie konfiguračných údajov aplikácie (t. J. Pripojenie k úložisku). Ak sa používajú ako globálne objekty, je ťažké zvoliť konfiguráciu testovacieho prostredia.

Preto keď spustíme testy, produkčná databáza sa pokazí s testovacími dátami, čo je ťažko prijateľné.

Ak potrebujeme singleton, mohli by sme zvážiť možnosť delegovania jeho inštancie na inú triedu - akúsi továreň - ktorá by sa mala postarať o zabezpečenie toho, že v hre je iba jeden prípad singletonu.

5.2. Implementačné problémy

Aj keď sa singlety javia ako celkom jednoduché, ich implementácia môže trpieť rôznymi problémami. Všetko vedie k tomu, že by sme mohli mať viac ako jednu inštanciu triedy.

Synchronizácia

Implementácia so súkromným konštruktorom, ktorú sme predstavili vyššie, nie je bezpečná pre vlákna: funguje dobre v prostredí s jedným vláknom, ale vo viacvláknovom by sme mali na zabezpečenie atomicity operácie použiť techniku ​​synchronizácie:

verejný synchronizovaný statický ClassSingleton getInstance () {if (INSTANCE == null) {INSTANCE = nový ClassSingleton (); } návrat INSTANCE; }

Poznačte si kľúčové slovo synchronizované v deklarácii metódy. Telo metódy má niekoľko operácií (porovnanie, vytvorenie inštancie a návrat).

Pri absencii synchronizácie existuje možnosť, že dve vlákna prekladajú svoje vykonanie takým spôsobom, že výraz INSTANCE == null hodnotí na pravda pre obidve vlákna a vo výsledku dva prípady TriedaSingleton byť stvorený.

Synchronizácia môže významne ovplyvniť výkon. Ak sa tento kód vyvoláva často, mali by sme ho urýchliť pomocou rôznych techník ako lenivá inicializácia alebo dvakrát skontrolované uzamknutie (uvedomte si, že to kvôli optimalizácii kompilátora nemusí fungovať podľa očakávaní). Viac podrobností si môžeme pozrieť v našom výučbe „Zamknutie s dvojitou kontrolou pomocou Singletonu“.

Viaceré inštancie

Existuje niekoľko ďalších problémov so singletonmi, ktoré súvisia so samotným JVM a ktoré by mohli spôsobiť, že skončíme s viacerými prípadmi singletonu. Tieto problémy sú celkom jemné a ku každému z nich uvedieme stručný popis:

  1. Singleton má byť podľa JVM jedinečný. To môže robiť problém distribuovaným systémom alebo systémom, ktorých vnútorná časť je založená na distribuovaných technológiách.
  2. Každý zavádzač tried môže načítať svoju verziu singletonu.
  3. Jeden človek môže byť zhromažďovaný odpadkami, akonáhle o ňom nikto nebude hovoriť. Tento problém nevedie k prítomnosti viacerých inštancií singletonu súčasne, ale pri opätovnom vytvorení sa inštancia môže líšiť od predchádzajúcej verzie.

6. Záver

V tomto rýchlom výučbe sme sa zamerali na to, ako implementovať Singletonov vzor iba pomocou základnej Javy, a ako sa uistiť, že je konzistentný, a ako tieto implementácie využívať.

Plnú implementáciu týchto príkladov nájdete na GitHub.