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:
- 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.
- Minden osztályos betöltő betöltheti a szingulett verzióját.
- 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.