Singletons Java-ban

1. Bemutatkozás

Ebben a rövid cikkben a Singletons egyszerű Java-ban történő megvalósításának két legnépszerűbb módját tárgyaljuk.

2. Osztályalapú Singleton

A legnépszerűbb megközelítés a Singleton bevezetése egy rendes osztály létrehozásával és annak biztosításával, hogy:

  • Magánépítő
  • Statikus mező, amely egyetlen példányát tartalmazza
  • Statikus gyári módszer a példány megszerzésére

Hozzáadunk egy információs tulajdonságot is, csak későbbi felhasználás céljából. Tehát a megvalósításunk így fog kinézni:

public final class ClassSingleton {private static ClassSingleton INSTANCE; private String info = "Kezdeti információ osztály"; privát ClassSingleton () {} public static ClassSingleton getInstance () {if (INSTANCE == null) {INSTANCE = új ClassSingleton (); } return INSTANCE; } // szerelők és beállítók}

Bár ez általános megközelítés, fontos megjegyezni, hogy ez többszálas forgatókönyvek esetén problémás lehet, ami a Singletons használatának fő oka.

Egyszerűen fogalmazva, több példányt is eredményezhet, ami megsérti a minta alapelvét. Bár erre a problémára vannak záró megoldások, a következő megközelítésünk gyökér szinten oldja meg ezeket a problémákat.

3. Enum Singleton

Továbblépve ne beszéljünk egy másik érdekes megközelítésről - amely a felsorolások használata:

public enum EnumSingleton {INSTANCE ("Initial class info"); privát karakterlánc információ; privát EnumSingleton (String info) {this.info = info; } public EnumSingleton getInstance () {return INSTANCE; } // szerelők és beállítók}

Ennek a megközelítésnek a sorosítását és a szálbiztonságot maga az enum implementáció garantálja, amely belsőleg biztosítja, hogy csak egyetlen példány érhető el, kijavítva az osztályalapú megvalósításban rámutatott problémákat.

4. Használat

Hogy használjuk a mi ClassSingleton, egyszerűen statikusan meg kell kapnunk a példányt:

ClassSingleton classSingleton1 = ClassSingleton.getInstance (); System.out.println (classSingleton1.getInfo ()); // Kezdeti osztályinformációk ClassSingleton classSingleton2 = ClassSingleton.getInstance (); classSingleton2.setInfo ("Új osztályinformáció"); System.out.println (classSingleton1.getInfo ()); // Új osztályinformáció System.out.println (classSingleton2.getInfo ()); // Új osztály információ

Ami a EnumSingleton, használhatjuk, mint bármely más Java Enum-ot:

EnumSingleton enumSingleton1 = EnumSingleton.INSTANCE.getInstance (); System.out.println (enumSingleton1.getInfo ()); // Kezdeti enum információ EnumSingleton enumSingleton2 = EnumSingleton.INSTANCE.getInstance (); enumSingleton2.setInfo ("Új enum információ"); System.out.println (enumSingleton1.getInfo ()); // Új enum információ System.out.println (enumSingleton2.getInfo ()); // Új enum információ

5. Gyakori csapdák

A Singleton megtévesztően egyszerű tervezési minta, és kevés olyan gyakori hiba fordul elő, amelyet a programozó elkövethet a szingulett létrehozásakor.

Kétféle problémát különböztetünk meg a szinguletteknél:

  • egzisztenciális (szükségünk van egy szingulettre?)
  • implementációs (helyesen hajtjuk végre?)

5.1. Egzisztenciális kérdések

Fogalmilag a szingulett egyfajta globális változó. Általánosságban tudjuk, hogy a globális változókat el kell kerülni - főleg, ha állapotuk mutábilis.

Nem azt mondjuk, hogy soha ne használjunk szinguletteket. Azt mondjuk azonban, hogy hatékonyabb módszerek lehetnek a kód rendezésére.

Ha a módszer megvalósítása egy szingulett objektumtól függ, miért nem adja át paraméterként? Ebben az esetben kifejezetten megmutatjuk, hogy mitől függ a módszer. Ennek eredményeként könnyen kigúnyolhatjuk ezeket a függőségeket (ha szükséges) a tesztelés során.

Például a szinguletteket gyakran használják az alkalmazás konfigurációs adatainak (azaz az adattárhoz való kapcsolódás) átfogására. Ha globális objektumként használják őket, akkor nehézséget okoz a tesztkörnyezet konfigurációjának kiválasztása.

Ezért a tesztek futtatásakor a termelési adatbázis elrontja a teszt adatait, ami aligha elfogadható.

Ha szükségünk van szingulettre, akkor fontolóra vehetjük annak lehetőségét, hogy egy másik osztályra - egyfajta gyárra - delegáljuk a példányt, amelynek gondoskodnia kell arról, hogy a szingulettnek csak egy példánya legyen játékban.

5.2. Végrehajtási kérdések

Annak ellenére, hogy a szingulettek meglehetősen egyszerűnek tűnnek, megvalósításuk különféle problémáktól szenvedhet. Mindez azt a tényt eredményezi, hogy végül nem csak az osztály egy példánya lesz.

Szinkronizálás

A fent bemutatott magánkonstruktorral történő megvalósítás nem szálbiztonságos: egyszálas környezetben jól működik, de többszálú környezetben a szinkronizálási technikát kell használnunk a művelet atomosságának garantálásához:

nyilvános szinkronizált statikus ClassSingleton getInstance () {if (INSTANCE == null) {INSTANCE = új ClassSingleton (); } return INSTANCE; }

Jegyezze fel a kulcsszót szinkronizált a módszer deklarációjában. A módszer testének több művelete van (összehasonlítás, példányosítás és visszatérés).

Szinkronizálás hiányában fennáll annak a lehetősége, hogy két szál úgy fonja egymásba végrehajtásaikat, hogy a kifejezés INSTANCE == null értékeli, hogy igaz mindkét szálhoz, és ennek eredményeként a ClassSingleton létrejön.

Szinkronizálás jelentősen befolyásolhatja a teljesítményt. Ha erre a kódra gyakran hivatkoznak, akkor fel kell gyorsítanunk különféle technikák, például lusta inicializálás vagy kétszeresen ellenőrzött reteszelés (ne feledje, hogy ez a fordító optimalizálása miatt nem biztos, hogy a várt módon működik). További részleteket a „Double-Checked Locking with Singleton” című oktatóanyagunkban láthatunk.

Több példány

Magával a JVM-mel kapcsolatban számos más probléma merül fel, amelyek miatt a szingulettnek több példánya is előfordulhat. Ezek a kérdések meglehetősen finomak, és röviden leírjuk mindegyiket:

  1. Egy szingulett állítólag JVM-enként egyedi. Ez problémát jelenthet az elosztott rendszerek vagy olyan rendszerek számára, amelyek belső elosztott technológiákon alapulnak.
  2. Minden osztályos betöltő betöltheti a szingulett verzióját.
  3. Lehet, hogy egy szingulettet szemét gyűjt, ha senki sem rendelkezik rá hivatkozással. Ez a probléma nem vezet egyidejűleg több egyedi példány jelenlétéhez, de újra létrehozva a példány eltérhet az előző verziójától.

6. Következtetés

Ebben a gyors oktatóanyagban arra összpontosítottunk, hogy miként valósítsuk meg a Singleton mintát csak a Java alapmotorjával, és hogyan győződjünk meg arról, hogy következetes-e, valamint hogyan használjuk fel ezeket a megvalósításokat.

Ezeknek a példáknak a teljes megvalósítása megtalálható a GitHub oldalon.