Multiton (nebo také originál) je v softwarovém inženýrství návrhový vzor ze skupiny řízení tvorby instancí. Vzor má za cíl řídit počet vytvořených instancí v systému a to pomocí párování k unikátním klíčům.
Popis alt=Jednoduchý UML diagram vzoru Multiton bez použití explicitního vyjádření klíče jinou třídou|náhled|Jednoduchý UML diagram vzoru Multiton Návrhový vzor originál se řadí mezi vzory starající se o tvorbu instancí, přičemž se zaměřuje na řízení počtu instancí. Toho dosahuje přiřazením vytvářených instancí klíči, přičemž na instanci třídy definující náš Multiton se dotazujeme právě tímto klíčem. Je zároveň dobrým zvykem vybírat jako tento klíč nejdůležitější atribut, který instanci náleží, a zároveň musí být hodnota tohoto atributu unikátní. Příkladně souřadnice na hrací ploše, unikátní název, identifikátor nebo nějaká klíčová kombinace.
Alternativní název originál vychází z vlastnosti, kdy při poptávce po instanci k danému klíči se a priori nevytváří nová instance, nýbrž se vrátí ta původní, originální.
Vlastnosti
Instance Multitonu Samotná instance je tvořena zpravidla atributem, který instanci tvoří. Příkladně může jít o souřadnice hrací desky šachů (kombinace číslice a písmene, čímž nám vznikne právě 64 instancí třídy Políčko) či například emailová adresa v seznamu kontaktů. Tento klíč ostatně nemusí být ani obsažen v samotné instanci originálu, avšak vždy platí, že instance klíče musí být unikátní, instance přiřazenému klíči musí být unikátní a unikátní musí být i tato vazba.
Konstruktor Jako je pro návrhové vzory starající se o tvorbu instancí příznačné, třídy mají omezený přístup ke konstruktoru instance, a to ideálně privátní (z třídy samotné). Konstruktor (či konstruktory) jsou volány obvykle jen přístupovou metodou (statickou) a nebo statickým iniciačním blokem.
Klíč Třída instancí, které jsou používány pro vyhledávání a identifikování objektů. V podstatě platí, že počet instancí klíče je menší nebo roven počtu instancí třídy originálu.
Klíč bývá zpravidla interpretován jako výčtový typ (pro neměnný počet instancí) nebo jako instance jiné třídy (například jméno v adresáři, podle kterého hledáme instanci třídy Osoba, jenž je reprezentována množinou telefonních čísel, e-mailových adres, dvojice souřadnic reprezentující políčko při hře Lodě). Druhý zmíněný způsob umožňuje rozšiřování sady instancí originálu (tedy přidávání osob do adresáře či rozšiřování hracího plánku) a to za běhu programu – dynamicky.
Alternativním a ne zcela vhodným způsobem je používání klíče, který není nutně spjat s povahou samotné instance originálu jejího stavu či její podoby, příkladně použití čísla coby označení pořadí instancí. Představit bychom si to mohli na příkladu vyhledávání kontaktu na osobu podle pořadí jeho zařazení do adresáře.
Je vhodné uvažovat situaci, kdy bude požadována instance k null, tedy kdy bude místo klíče vložen do přístupové metody prázdný objekt. Není-li to v zájmu logiky programu, měl by umět kód na takovou situaci v rámci přístupové metody adekvátně reagovat – vyhozením výjimky či automatickým vrácením prázdné instance (také null).
Kontejner instancí Pro uložení dvojic klíč-instance se obvykle používá mapy (slovníku), v němž je mapován klíč na referenci na instanci. Tento přístup umožňuje poměrně snadný a rychlý přístup k instanci, zná-li žadatel klíč.
Alternativním přístupem je využití výčtového typu, jehož instance v sobě obsahují odkazy na instance originálu. Výhodou je snadné použití, ovšem na úkor nebezpečí zveřejnění konstruktoru minimálně do stavu package-private. Další nevýhodou je zásadní omezení v případě dalšího vývoje, kdy je v některých jazycích (např. v Javě) nemožné rozšířit dynamicky sadu klíčů (rozšířením počtu instancí výčtového typu).
Typy Typy vzoru multiton jsou především dva:
Bez možnosti přidávání nových instancí – tento typ je mnohem jednodušší na implementaci a zpravidla bývá i bezpečnější. Pro tento typ však platí omezení, že počet instancí je striktně omezen na počet instancí klíče a není možné ho přesáhnout. Vhodné je tento způsob použít například ve hrách jako je sudoku či šachy, kde předem přesně známe neměnný počet polí (pro sudoku 81, pro šachy 64).
S možností přidávání nových instancí – mnohem variabilnější způsob, který umožňuje dynamicky ovlivňovat počet instancí typu multiton. Obvykle je však náročnější ho zabezpečit proti možným chybám (například pro vícevláknové aplikace). Tyto chyby mohou způsobit nekonzistenci celého systému, kdy dvě části programu odkazují na dvě odlišné instance se stejným klíčem. Jako příklad lze použít matriku, kde vyhledáváme jednotlivé osoby (instance multitonu) podle rodného čísla (klíč). Pokud lze registrovat na matriku novorozence z více míst najednou, je nutné zajistit, aby dva novorozenci (narození ve stejný okamžik) nedostali přidělené stejné rodné číslo (synchronizace vláken například pomocí uzamčení generátoru rodných čísel). Pokud by se to tak stalo, tak by v první řadě byl jeden z těchto novorozenců přepsán druhým (na matrice by přestal existovat) a zároveň by si stále zasahovali vzájemně do života.
Pro oba druhy však platí, že mají soukromý konstruktor a mají vlastní přístupovou metodu, která vrací odkazy na instance s příslušným klíčem.
Důvody k použití Návrhový vzor originál je dobré použít tam, kde víme, že počet instancí daného typu v systému bude malý, zato bude použit na mnoha místech v kódu.
Hlavními důvody pro nasazení návrhového vzoru originál mohou být následující:
Náročná (případně pomalá) tvorba instancí – například při čtení většího objemu dat z úložiště či stahování dat ze vzdálených zdrojů Instance jsou náročné na kapacity (například zabírají mnoho paměti) – například skládá-li se instance originálu z velkého datového objemu Business model aplikace vyžaduje jen omezený počet instancí – například v případě karetních her nechceme, aby bylo možné vytvářet nové karty mimo ty, které jsou v balíčku Business model aplikace vyžaduje znovupoužití instancí – například při postupném přiřazování nových hodnot k instanci s daným klíčem
Výhody použití
Zvýšení efektivity Při tvorbě "drahých" instancí (rozuměj instancí pomalých na tvorbu či náročných na kapacity) se pouze hledá odkaz. Navíc, použijeme-li klíčů, nemusíme porovnávat celé tyto objekty. Stačí nám porovnávat místo toho samotné klíče, které mohou být na porovnání rovnosti i řádově snazší a rychlejší.
Předcházení kolizím Lze použít tohoto vzoru pro zabránění tvorby identických instancí, které by však byly "dvojčaty". Tyto by měly sice měly shodný pomyslný klíč, ale na dvou místech by se pracovalo se dvěma odlišnými instancemi a tento postup mezi nimi by se pravděpodobně stejně musel následně jednotit.
Usnadnění použití Při implementaci se již neodkazuji pomocí instance, nýbrž pomocí klíče. Stačí mi tedy jen aby byl programátorovi znám samotný klíč, nemusí již pracovat s danou instancí, případně vkládat všechny parametry potřebné pro tvorbu instance do konstruktoru.
Spřízněné vzory Stejně jako pro jiné vzory ze skupiny Creational patterns (vzory řízení tvorby instancí) platí, že mají soukromé konstruktory a zároveň nějakou formu přístupové metody.
Návrhový vzor Singleton (Jedináček) je v podstatě speciální druh vzoru Multiton, neboť je omezen počet instancí na maximálně jednu a tím pádem není třeba žádných klíčů. Vedle toho vzor Factory method (tovární metoda), která se stará o řízení tvorby instancí, je v jisté podobě použita, a to v rámci líné inicializace. Zde se řídí, zda má být instance vytvořena, či vrácena již existující.
Vzor Multiton se liší od návrhového vzoru Fond (Object pool nebo Bazén) v následujících specifikách:
Funkce multitonu neřídí kdy kdo bude mít k daným instancím přístup. Fond je v tomto (v případě vytíženosti instancí, když žádná z instancí není volná) dvojího typu: Statický – zařazuje poptávky do fronty a ty postupně obsluhuje Dynamický – v případě, že není žádný objekt volný, vytvoří nový a ten předá pro vyhovění poptávce. Je pak na konkrétní implementaci, zda tento objekt zůstane mezi vytvořenými a dále k užití či bude postoupen destrukci.
Není jasná (a většinou ani důležitá) vazba mezi klíčem a instancí, na čemž vzor Multiton de facto stojí. V případě, že potřebujeme fond, který by tuto vlastnost měl, je vhodné se uchýlit přímo k multitonu (pro statickou formu) či prohlásit instanci reprezentující fond coby instanci multitonu, tedy pro každý klíč máme celý fond instancí (pro potřeby dynamické formy).
Ukázky implementace Následující ukázky jsou uvedeny v jazyce Java.
Příklad obecné implementace (bez přidávání prvků) alt=UML diagram vzoru s použitím výčtového typu jako klíče|náhled|400x400pixelů|UML diagram vzoru s použitím výčtového typu jako klíče Uvažujeme-li případ bez přidávání prvků, tedy že nebudeme dynamicky (za běhu programu) upravovat množství instancí třídy Multiton, můžeme veškerou odpovědnost za přípravu prvků přenechat na statickém iniciačním bloku v kombinaci s výčtovým typem. Tato kombinace umožňuje naplnění kontejneru metodou eager initialization (hladová inicializace), které se provede v okamžiku prvního zmínění třídy. Alternativně by bylo možné použít metody lazy initialization, kterou by bylo vhodné implementovat tak, že při dotazu na instanci k danému klíči se zkontroluje, zda tato relace existuje. Pokud ne, vytvoří se tato instance a uloží do kontejneru s přiřazeným klíčem. V každém případě se příslušnému klíči vrátí instance multitonu. U líné inicializace je však nutné ohlídat bezpečnost při aplikaci s více vlákny, neboť by mohlo dojít ke konzistenčním trhlinám.
Na ukázkových příkladech je kontejner obsahující klíč a instanci multitonu reprezentován mapou (slovníkem či asociativním polem), čímž umožňuje vyhledat instanci (Multiton) podle zadaného klíče (Key). Klíč může mít 5 různých podob, v tomto případě KEY_0, KEY_1, KEY_2, KEY_3, a KEY_4. To znamená, že bude existovat maximálně pět instancí této třídy. Požaduje-li klient instanci podle vzoru klíče KEY_3, pak zavolá přístupovou metodu, do které v parametru vloží právě tento klíč a tato metoda vrátí příslušnou instanci (zde je tato metoda reprezentována pomocí statické metody getInstance(Key)).
Eager inicializace Na následujícím výpise je uvedena ukázka obecného vzoru Multiton v podobě hladové inicializace.
package Multiton.general;
import java.util.HashMap; import java.util.Map;
/** * Třída Multiton reprezentující hlavní objekt vzoru Multiton. * * Tato třída obsahuje statickou mapu (slovník) složený z klíčů (instancí výčtového typu Key) * a hodnot reprezentovaných instancemi třídy Multiton. * * Dále obsahuje soukromý konstruktor a statickou metodu getInstance(Key), která vrací pro daný * klíč jedinou instanci. */ public class Multiton {
/** Klíče, podle kterých je vyhledávána instance třídy Multiton */ public enum Key {
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4;
/** Klíčový atribut reprezentující instanci Multiton */ private final Key key;
/** Kontejner všech existujících instancí přiřazených k vlasním klíčům */ private static Map<Key, Multiton> MULTITONS = new HashMap<>();
static {
for (Key key : Key.values()) {
MULTITONS.put(key, new Multiton(key));
/** * Soukromý konstruktor třídy Multiton, který zabraňuje vyrábění instancí mimo tuto třídu
* @param key Klíč, podle kterého bude vyhledáváno. Jde o nejdůležitější atribut * instance a je mezi ostatními instancemi třídy Multiton unikátní. */ private Multiton(Key key) {
this.key = key;
/** * Statická metoda getInstance, která je odpovědná za vrácení instance, která jako jediná * má shodný atribut s tím, který byl vložen.
* @param key klíč, který je uložen jako atribut v rámci instance Multiton
* @return jedinou instanci třídy Multiton, která má tento atribut */ public static Multiton getInstance(Key key) {
return MULTITONS.get(key);
Lazy inicializace