Neobsahuje názory Henryka Laholy

K čemu singleton?

Komentář k Davidovu článku o singletonech v PHP, který trošku nakynul, tentokrát na téma "Proč vytvářet třídu, když může mít jen jednu instanci?" aneb Základy návrhových vzorů v PHP

Tomík se ptá: Můžu se zeptat, když už jsme u těch singletonů, zda jej někdo z vás někdy opravdu použil, myslím, kromě takových těch papírových příkladů, opravdu v praxi?

Vím, že toto existuje, ale nikdy jsem se neocitl v situaci, kdy bych jej potřeboval, nebo by mi usnadnil život. Ptám se tedy, zda jsem o něco přišel? Nebo mi něco uniká?

Chci tím říct, neztrácí se pak vpodstatě kouzlo OOP? Proč vytvářet třídu, když může mít jen jednu instanci, nemůže se dědit, klonovat?

Myslím, že asi vždy může nastat situace, kdy je dobré mít min. dvě instance jedné třídy.

Začal jsem psát odpověď k Davidovi, ale trošku nakynula, takže ji dám sem.

Ano, použil, několikrát.

Dám příklad, ze života, z PHP a od webů: Doby, kdy si každý kus kódu vypsal na výstup co se mu zachtělo jsou, zaplaťpámbu, pryč. Přesto výstup, holt, vzniká v různých místech kódu. Je třeba mít nějaké místo, kde si mohu tyto data schraňovat a na konci je předat patřičnému systému ("View" terminologií MVC, šablonovacímu systému v terminologii mojí). Je jasné, že takto uložená a připravená data musí být "globálně" a "jednou". Jako ideální řešení se nabízí statická třída. Jenže já chci na konci všechna data předhodit šablonovacímu nástroji. Stále by to ještě šlo, ale už to poměrně dře, v template engine bude muset být všude odkazováno přes Třída::$proměnná... což navíc předpokládá public proměnné, takže lepší řešení je Třída::get('proměnná')... všude a natvrdo. A co když se rozhodnu výstup dat zahodit a "podhodit" nějaký testovací soubor dat? Budu je všechna přepisovat?

A co když mám (a já mám...) víc šablonových nástrojů? Budu do všech psát tyhle obezličky?

Já si pro tento výstup udělám objekt třídy, řekněme, Response. Třída Response je v zásadě jednoduchá, implementuje jen obecný setter, getter a pak nějakou metodu "vrať vše jako pole" či tak něco. Tam, kde vznikne potřeba poslat nějaké údaje ven, si vezmu instanci Response a nacpu do ni patřičná data. A na konci běhu celý tenhle objekt i se všemi jeho daty předhodím obecnému šablonovacímu nástroji, který očekává na vstupu objekt, co umí "getDataAsArray" (interface IArrayable) ;) – asi takhle: $template->data($response). Zkusíte to nasimulovat se statickou třídou?

U tohoto objektu, který udržuje data, co mají jít na výstup, jsem přesvědčen, že by opravdu nebylo dobré mít dvě instance téže třídy. A nebylo by dobré nemít žádnou instanci třídy. Je ideální mít jednu a přesně jednu, a je dobré mít ji jako objekt. (A to, jestli je instance uložena ve statické privátní proměnné nebo v globálním skladu, je celkem putna.)

Jiný příklad: Parsuju si na začátku URL a podle výsledků volím kontroléry a parametry apod. Může to udělat statická třída... Jenže já chci k těm parametrům přistupovat i z jiných tříd... Mohu psát URL::$path, ale pak si musím $path definovat jako veřejnou, což NENÍ DOBRÉ. Definuju si ji jako privátní, pak si ale musím napsat sadu statických getterů a setterů...

Zkrátka: Singleton je dobrý tam, kde chci mít:

1. Zabalená data, co k sobě patří
2. tak, abych s nimi mohl pracovat jako s jedním objektem
3. a s jistotou, že jsou v systému jen jednou, že si tedy například výstup neuložím do dvaceti různých instancí.

A proč místo něj nepoužít statickou třídu? Protože objekt má spoustu výhod: Může implementovat nějaké rozhraní, může být předán coby parametr a dokonce může být "produktem" nějaké Factory metody, takže lze až za běhu určit, čí instancí vlastně bude.

A PS: Ta Pecinovského kniha je OPRAVDU, ale OPRAVDU dobrá! Sice se věnuje především Javě, ale na otázky jako jsou ty Tomíkovy odpovídá velmi dobře.

Dne 22.08.2008

Twittni

Přidej do: Přidat na Conota Linkuj si ! asdf.sk StumbleUpon Toolbar Stumble It!

Komentáře

[1] (David Grudl - WWW) 22.08.2008, 16:06:55 [X] [D]
Budu s tebou polemizovat v zásadní věci: proč by se objekt Response měl rozhodnout a zajistit, že bude existovat jen jednou? Co se změní, když dáš třídě Response veřejný konstruktor a zrušíš všechna omezení na jeho jedinečnost? Nestane se nic. Tvá aplikace bude dál ukládat parametry do té jedné instance, umístěné na známém globálním místě - např. Response::getInstance(), nebo Application::getResponseInstance() nebo dokonce $GLOBALS['response'].

Rozlišujme potřebu globálně dostupného úložiště a potřebu unikátnosti instance. Dle mého to druhé je často neopodstatněné, vede ke zbytečným komplikacím při testování a není ani v "pravomoci" objektu takto globální prostor omezovat.

[2] (Arthur Dent [openID] - Mail - WWW) 22.08.2008, 17:20:04 [X] [D]
[1] Trošku jsme si asi nerozuměli. Já nepolemizoval s tvým "mechanismus unikátnosti v třídě" vs "jiné mechanismy zajištění unikátnosti". Tomík se ptal na to, jaký vůbec smysl má třída, která má jednu a právě jednu instanci, tak jsem se mu snažil vysvětlit, že taková třída praktický smysl má.

To, jestli má privátní konstruktor a getInstance, nebo jestli je objekt vytvářen nějakou globální Factory/Fondem nebo jestli si ho ukládám do globals je (v tuto chvíli a pro potřeby tohoto článku) jedno. Otázka, jak jsem ji pochopil, byla na SMYSL třídy, která má právě jedenu instanci.

Takže polemizuješ s něčím, co neříkám. Já říkám, že SI VEZMU tu JEDNU instanci, nespecifikuju jak, jestli o ní má rozhodnout přímo třída Response nebo jestli to zajistím via globální úložiště, o to nejde a to je téma do jiné diskuse (do té tvé). Jde o to, že má smysl, když v systému existuje JEDNA INSTANCE. ;) Ostatně třeba i to globální úložiště, pokud nebude čistě statické, bude singleton.

Zkus brát "singleton" nikoli jako konkrétní implementaci, ale jako "přístup", jako "vzor" - "Třída s jednou instancí".

[3] (David Grudl - WWW) 22.08.2008, 17:35:39 [X] [D]
[2] Tomík se ptal na to, jaký vůbec smysl má třída, která má jednu a právě jednu instanci, a ty jsi jako příklad uvedl objekt, který je globálně dostupný - tedy bez ohledu na to, kolik může mít v systému najednou instancí. Nebo v čem se pletu?

[4] (David Grudl - WWW) 22.08.2008, 17:38:17 [X] [D]
Ještě chci zdůraznit to "jednu a právě jednu instanci".

Píšeš :
> Jde o to, že má smysl, když v systému existuje JEDNA INSTANCE. ;) Ostatně třeba i to globální úložiště, pokud nebude čistě statické, bude singleton.

To chápu jako odpověď na "existuje jedna instance", ale ne už jako odpověď na "jedna a právě jedna instance".

[5] (Arthur Dent [openID] - Mail - WWW) 22.08.2008, 18:10:59 [X] [D]
[3] Odpovídal jsem na Tomíkovu otázku: "Proč vytvářet třídu, když může mít jen jednu instanci, nemůže se dědit, klonovat?" Pokud bych chtěl šťourat, tak napíšu, že už v Tomíkově otázce je chyba (i od singleton třídy lze odvodit a "podědit"). Ale já jsem si z toho vzal tu část, kde se podivuje nad užitečností třídy, co má jen jednoho objekta. A o to Tomíkovi zjevně šlo, protože o kousek dál říká, že "asi vždy může nastat situace, kdy je dobré mít min. dvě instance jedné třídy."

Uvedl jsem příklad, kdy je vhodné vytvořit třídu, která má v aplikaci (v jednom "request běhu" či jak to nazvat) jednu instanci. Nic z toho, na co ses ptal v [1][3][4] jsem neřešil, protože na to jsem odpovídat nechtěl. Má odpověď se vztahovala na situaci, kdy je lepší místo statické třídy se statickými proměnnými použít třídu, u které:
- je možno vytvořit instanci
- je žádoucí, aby po dobu běhu byla vytvořena jen jedna instance.

---

Pokud hovoříš o něčem, o čem píšu já, tak je mi fakt líto, ale netuším, kam míříš. Mám totiž intenzivní dojem, že každý mluvíme o jiném kousku problému. Já tvé poznámky chápu tak, že je zbytečné dělat ve třídě singletonové opičárny, když můžeš zajistit, aby v systému existovala jednou jiným způsobem (např. tak, že si uložíš její instanci kamsi globálně). O tom se nepřu a k tomu se nevyjadřuju, k tomu je diskuse u tebe. Já se vyjadřuju k té Tomíkově otázce, tedy "K čemu je třída, která má jen jednu instanci?"

Davide, NA TO se Tomík ptá a NA TO mu odpovídám: Je dobré ji mít na věci, které se v systému vyskytují jen jednou (třeba to Response) a pokud je potřeba ji někdy třeba předat jako parametr nějaké metodě.

A prosím neřešme, jestli globálně nebo "samoregulačním mechanismem" nebo tak či onak. Otázka je "Jaký má jednoinstanční třída smysl", ne "jak zajistit že bude jen jednou".

Ale jestli máš dojem, že opravdu polemizuješ s tím co píšu, tak to zkus ještě jednou a, prosím, jak pro blbce, protože já fakt nerozumím. :)

[6] (David Grudl - WWW) 22.08.2008, 18:24:56 [X] [D]
[5] Tomíkovi jsi určitě dal dobrou odpověď, dokonce třídu Response jsem původně chtěl ve svém článku použít místo Database.

Titulní téma je singleton, tedy třída, která (mimojiné) může mít jednu a právě jednu instanci. Tvé vysvětlení pokrývá celé téma kromě toho "právě jednu instanci". Bez "právě jednu instanci" se vlastně nemluví o singletonu, ale o registry nebo jak se těm věcem říká (otázka na Reného). Titulek článku mohl znít "K čemu registry" a celý mohl zůstat tak jak je.

[7] (Arthur Dent [openID] - Mail - WWW) 22.08.2008, 19:43:59 [X] [D]
[6] Registry nejsou. :) A "právě jedna instance" je dobrovolné omezení proti zmatkům a nejednoznačnostem; globální dostupnost je až vedlejší efekt.

[8] (klok - WWW) 22.08.2008, 20:45:20 [X] [D]
[6] ...Titulní téma je singleton, tedy třída, která (mimojiné) může mít jednu a právě jednu instanci...

Odpoved jsem psal u tebe. Primarnim smyslem singletonu je mit jednu instanci a tim zajistovat pristup k unikatnim zdrojum ktere je treba spravovat. Souhlasim s tim ze v PHP dochazi k jeho naduzivani na veci ktere lze resit jinak.

Ale take tvrdim, ze diky "konvencnosti" tohoto patternu je programatorum (alespon vetsine ;-) zrejme, ze neni vhodne vytvaret vice instanci dane tridy.

A co se tyce potreby jinych "singletonu" pro potreby testovani, to lze resit napriklad pomoci Factory, ktera mi bude vracet jinou instanci singletonu.

[9] (klokane [openID] - WWW) 22.08.2008, 20:59:14 [X] [D]
sry. samozrejme ze ne "jinou instanci singletonu" ale "instanci jineho singletonu" se stejnym rozhranim

[10] (Michal Aichinger - WWW) 22.08.2008, 22:36:55 [X] [D]
Mě se zdá že David nemá rád jakékoli svazování konvencemi :) Prostě singleton je o konvencích. Pokud programátor znalý návrhových vzorů uvidí nějakou třídu s privátním konstruktorem a metodou pro vytvoření objektu a poznámkou, že jde o singleton, tak ví, že autor chtěl aby byla instanciována pouze jednou.

Problém vidím taky v tom, že normálně kód ovlivňují programátoři, ale u Davida jde asi o genetické programování a program se vytváří sám, když pořád píše o tom, že se objekty rozhodují samy :)

[11] (Tomik - Mail - WWW) 23.08.2008, 14:06:08 [X] [D]
Díky, Arthure! Přesně takovou odpověď jsem si představoval! :)

Jinak, co se týče testů, myslím, že se to opravdu dá vyřešit jiným singletonem se stejným rozhraním (viz. [8]).

P.S.: Jsem Tomik, nikoli Tomík. :)