Az egyik legegyszerűbb létrehozási minta és ebből adódóan ha tervezési minták szóbakerülnek, akkor általában ez szokott lenni az első, ami előkerül. Ez a minta akkor tudna hasznos lenni, ha a programunk rendellenesen működhetne, összeomolhatna, ha egy bizonyos osztályból több példány is lehetne az alkalmazásunkban.
ElÅ‘nye, hogy nem készÃtünk feleslegesen globális változókat, valamint lehetÅ‘vé teszi a lusta allokációt. Ez azt jelenti, hogy a Singleton esetén lehetÅ‘ségünk van akkor létrehozni az adott osztálypéldányt, amikor elÅ‘ször szükség van rá.
A Singleton legegyszeűbb implementációja:
internal class Singleton
{
private Singleton()
{
}
private static Singleton _instance;
public static Singleton Instance
{
get
{
if (_instance == null)
_instance = new Singleton();
return _instance;
}
}
}
Az osztály konstruktora privát elérésű. Ez biztosÃtja, hogy a new() operátorral ne lehessen az objektumot tetszÅ‘legesen példányosÃtani. Az egyetlen példányt az osztály Instance tulajdonságán tudjuk elérni, ami elsÅ‘ használatkor hozza létre a példányt ténylegesen.
A bevezetésben használt "tudna hasznos lenni" kifejezés használata nem a véletlen műve. A singleton tervezési mintáról bebizonyosodott az évek alatt, hogy inkább egy anti pattern. Anti patternnek akkor nevezünk egy tervezési mintát, amikor több problémát okoz, mint amit megold.
A minta hátránya, hogy tesztelni nem igen lehetséges, vagy minimum nagyon megnehezÃti azt. A másik hátránya, hogy a single responsibility elvet sérti, hiszen az osztály két valamiért is felel: menedzseli az életciklusát és még valami funkciót biztosit.
Gyakori tendencia, hogy a singleton osztályok elÅ‘bb-utóbb az idÅ‘ múlásával úgynevezett isten osztállyá (god class) válnak. Az ilyen osztályok ismertetÅ‘ jele, hogy bÅ‘ven több mindent csinálnak, mint amit kellene nekik és elÅ‘bb-utóbb egy minimális módosÃtás törheti az egész szoftver működését.
Nyelvi szinten még egy ellenérv a singleton használata ellen, hogy nem szálbiztos. Az Instance tulajdonság lekérése közben kialakulhat egy versenyhelyzet, ha minimális idÅ‘eltéréssel két szál hozzá akar férni és a példány még nem létezik. Ekkor két lehetséges kimenet történhet. ElsÅ‘ opcióként történhet az, hogy csak egy példány fog létezni, DE nem az a szál lesz a tulajdonosa, mint aminek lennie kellene, ezért késÅ‘bb kivételek keletkezhetnek az alkalmazásban. A másik eset az, hogy mindkét szál sikerrel jár a példányosÃtásban és valójában két példány is lesz az alkalmazásunkban.
Bármelyik is történjen, mindkét eset egyformán rossz és bőven sok fejfájást tud okozni egy ilyen hiba kivizsgálása és megtalálása. Éppen ezért, ha többszálú környezetben is használva lesz a singleton osztályunk, akkor szálbiztossá kell tenni:
internal class ThreadSafeSingleton
{
private ThreadSafeSingleton()
{
}
private static readonly object _lock = new object();
private static ThreadSafeSingleton _instance;
public static ThreadSafeSingleton Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new ThreadSafeSingleton();
}
}
}
return _instance;
}
}
}
A fenti példában az implementáció egy kétszeres ellenÅ‘rzés és zárolás teszi szálbiztossá. A program indÃtása után elsÅ‘ ellenÅ‘rzésen mindkét szál túl fog jutni, de a lock (_lock) utasÃtás a második szálat várakozásra fogja kényszerÃteni. Mivel ekkor még nem létezik a példány, az ellenÅ‘rzésen túljutva az elsÅ‘ szál létrehozza az objektumot, majd a zárolás elengedése után a második szálnak is lehetÅ‘sége lesz belépni a lock (_lock) blokkba, de az már nem fog létrehozni példányt, mivel a korábbi szál már létrehozta az objektumot.
Előnyök
- Az osztálynak csak egyetlen példánya lehet
- Globális hozzáférés az adott példányhoz
- Csak akkor inicializálódik, amikor első alkalommal elkérjük a példányt
Hátrányok
- Sérti a Single Responsibility elvet, mivel két dolgot biztosÃt: egyetlen példány és globális hozzáférés
- Elfedheti a rossz tervezést, például amikor a program összetevői túl sokat tudnak egymásról.
- Különleges körültekintést igényel többszálú alkalmazások esetén
- Nehéz vagy lehetetlen egységtesztelni