Singleton (jedináček)

Návrhové vzory Singleton (jedináček)

Singleton je populární návrhový vzor, který umožňuje zajistit globální přístup k instanci nějaké třídy.

Motivace

Někdy v programu potřebujeme sdílet jednu instanci mezi několika bloky, objekty atd., aniž bychom ji museli stále předávat v konstruktoru. Ukázkový příklad je databázové připojení, celý program pracuje s jedním připojením a bylo by nepraktické ho stále předávat. Nabízí se řešení udělat třídu poskytující databázové API jako statickou. Může však nastat případ, kdy se nám hodí ji mít instanciovatelnou (např. někdy pracujeme s více připojeními) nebo používáme hotovu třídu, která statická není. Vložíme ji tedy do Singletonu.

Vzor

Vzor je tvořen třídou, která se stará o to, aby její instance existovala jen jednou.

Jako první musíme uživateli zakázat tvořit instanci, toho docílíme implementací prázdného privátního konstruktoru.

Dále vytvoříme běžnou instanční proměnnou a do ní vložíme instanci, kterou chceme v programu sdílet. V našem případě tedy instanci databázového připojení.

Nyní si třída vytvoří instanci sebe sama a tu uloží do statické proměnné. Instanci má takto ve správě třída a uživatel se k ní jinak než přes ni nedostane, protože ji nemůže vytvořit. Máme ji tedy zcela pod kontrolou. Instanci nastavíme samozřejmě jako privátní a také pouze pro čtení.

Nakonec vytvoříme veřejnou metodu, přes kterou budeme zvenku k instanci přistupovat. Uvnitř instanci vrátíme.

Ukázkový zdrojový kód (C#)

class Singleton
{
        private Singleton() { }

        public Databaze databaze = new Databaze('host', 'jmeno', 'heslo');

        private static readonly Singleton instance = new Singleton();

        public static Singleton vratInstanci()
        {
                return instance;
        }
}

Použití v programu

K připojení poté v programu přistupujeme takto:

Pripojeni pripojeni = Singleton.vratInstanci();

Singleton znamená jedináček, tedy instance, která nemá žádné sourozence.

Možné modifikace

Metoda k navrácení instance přímo vybízí k tomu, abychom inicializaci instance provedli až ve chvíli, kdy ji potřebujeme. Kód tedy můžeme modifikovat takto:

class Singleton
{
        private Singleton() { }

        public Databaze databaze = new Databaze('host', 'jmeno', 'heslo');

        private static Singleton instance = null;

        public static Singleton vratInstanci()
        {
                if (instance == null)
                        instance = new Singleton();
                return instance;
        }
}

Někdy takto můžeme optimalizovat výkon programu. Verze není thread safe a v případě, že bychom pracovali s více vlákny, je třeba dát inicializaci instance do locku.

Nevýhody

Slovo globální může být poněkud kontroverzní a Singleton je kvůli tomu někdy označován jako anti-pattern, tedy špatný vzor. Myslím však, že to není tak horké a ačkoli existují i jiná řešení, Singleton by měl každý programátor znát. Lepším vyřešením závislostí je vzor Dependency Injection.


 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (10 hlasů) :
4.14.14.14.1 4.1


 


Miniatura
Předchozí článek
MVC architektura
Miniatura
Všechny články v sekci
Návrhové vzory
Miniatura
Následující článek
Adapter (wrapper)

 

 

Komentáře
Zobrazit starší komentáře (2)

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Nene, ta proměnná je právě instanční. Celá krása Singletonu je v tom, že je to vlastně instance. Ty jen zakážeš uživateli, aby ji tvořil a spravuješ si ji sám pomocí statiky. Může to být trochu matoucí, ale třída Singleton si ve statice drží instanci třídy Singleton. Ta instance má normálně instanční proměnné.

Odpovědět 24.8.2012 11:57
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Opravil jsem 2. kód, bylo tam místo instance databaze. Třídu dále není vhodné označovat jako public, na což jsme s David Jančík [sczdavos]em přišli, protože poté není možné v singletonu uchovávat třídy bez modifikátoru.

Nyní je vše ozkoušené.

Odpovědět 24.8.2012 12:53
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Mario
Neregistrovaný
Avatar
Mario:

{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}

 
Odpovědět 17.1.2013 10:56
Avatar
Mario
Neregistrovaný
Avatar
Mario:

Jenom podotknu, ze C# ma super funkcionalitu vlastnosti, kterou tady jde pekne vyuzit. Priklad:

public static Singleton Instance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}

 
Odpovědět 17.1.2013 10:58
Avatar
Kit
Redaktor
Avatar
Odpovídá na Mario
Kit:

Od doby, kdy jsem přišel na Dependency Injection, doporučuji Singleton nepoužívat. Je k ničemu a špatně se testuje.

Editováno 17.1.2013 11:49
Odpovědět 17.1.2013 11:47
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
lcet.m
Člen
Avatar
lcet.m:

Vůbec nechápu, k čemu je ta public Databaze? Jako aby šla kdykoliv odkudkoliv z programu přepsat? Tak to je přesně to, čemu se snaží singleton zabránit.

Vůbec tu nějak často vídám public fields, což jde proti zásadám OOP. V případech, kdy jde v podstatě o konstanty, používat alespoň readonly. Správně to říká @Mario@

public class Singleton
{

   private Singleton(){}

   private static Singleton _inst = null;

   public static Singleton Current
   {
       get
       {
          _inst = _inst ?? new Singleton();
          return _inst;
       }
   }
}
 
Odpovědět 17.1.2013 15:05
Avatar
lcet.m
Člen
Avatar
Odpovídá na Kit
lcet.m:

O co hůř se testuje singleton, než DI?

 
Odpovědět 17.1.2013 15:09
Avatar
Kit
Redaktor
Avatar
Odpovídá na lcet.m
Kit:

Netuším, jak se píší mocky pro singleton, ale v DI je to hračka.

Singleton ani neřeší, když například chci otevřít dvě nebo více databází. Musím kvůli tomu vytvořit další třídy. V DI stačí další instance.

Odpovědět 18.1.2013 10:07
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
dlouhyjoosef
Člen
Avatar
dlouhyjoosef:

Dobry den. Nevim zda jsem to umistil spravne kdyztak prosim presunout. Mam na pad na zajimavy program ale neumim programovat mozna by to nekoho tady zaujalo a zkusil ho udelat. Resi se to pres maily nebo primo tady na foru ? Diky

 
Odpovědět  -1 22.1.2015 13:53
Avatar
zpavlu
Redaktor
Avatar
zpavlu:

Při procházení knihy „Modern C++ Design: Generic Programming and Design Patterns Applied“ od autora Andrei Alexandrescu jsem narazil na zajímavý problém pro otázku implementace Singletonu.
Knihu si můžeme stáhnout zde
O co se jedná:
• Máme tři Singletony pro řízení Klávesnice, Obrazovky a Deníku implementované jako Meyersovy Singletony.
• První dva modelují své protějšky a třetí slouží pro zápis chyb (na příklad textový soubor).
• Deník pro svou činnost potřebuje nějaké prostředky a proto bude vytvořen pouze objeví-li se nějaká chyba. Pokud není chyba, nebude Deník vytvořen.
• Program Deník oznamuje jakoukoliv chybu jak Klávesnice nebo Obrazovky .
• Předpokládejme, že po úspěšné implementaci Klávesnice se nepodaří inicializovat třídu Obrazovka. Pak konstruktor třídy Klávesnice inicializuje a vytvoří Deník, chyba je zaznamenána a program Deník je ukončen spolu s aplikací. Při ukončení jsou odstraněny lokální statické objekty v obráceném pořadí jejich vzniku. Deník je odstraněn před instancí Klávesnice. Pokud se z nějakého důvodu Klávesnice neodstraní, pak zkusí oznámit chybu programu Deník.
• Deník::Instance nevědomky vrací odkaz na slupku odstraněného objektu Deník. Náš program se začíná chovat nedefinovaně. Vzniká problém mrtvého odkazu.
• Pořadí odstraňování objektů Klávesnice, Deník a Obrazovka není definován. Je potřeba, aby Klávesnice a Obrazovka, poslední vytvořený je odstraněn jako první (pravidlo C++) a zároveň musíme z tohoto pravidla vyjmout Deník. Nezávisle na tom, kdy byl vytvořen musí být odstraněn poslední, aby mohl zachytit chyby předchozích obou.
Používáme-li v aplikaci více interagujících Singletonů, nelze používat automatickou kontrolu doby jejich existence. Dobře navržený Singleton by měl mrtvý odkaz detekovat.

Chtěl bych ukázat jak autor řešil tento návrhový vzor a jak postupně dospěl a implementoval generickou šablonu třídy SingletonHolder. Existují různé implementace Singletonů, jejich vhodnost závisí na problému, který řešíme. Veškeré ukázky jsou použité z této knihy bez úprav, autor je dal volně k dispozici. Totéž platí pro soubor „LokiLibrary.lib“ z knihovny „Loki“.

Pár slov k Mariovy, myslíš si,že když napíšeš kousek kódu bez jediné poznámky, že všichni pochopí smysl?

První díl najdete na mém webu.

Odpovědět 10. září 20:29
PZ
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 12. Zobrazit vše