JE_02 - Pomocí soukromého konstruktoru zajistěte vlastnost jedináčka

Jedináček je prostě třída, jejíž instance se vytváří právě jednou [Gamma95, str. 127]. Jedináčci typicky představují nějakou systémovou součást, která je sama o sobě jedinečná, jako je například grafické zobrazení nebo systém souborů.

Jedináčka lze implementovat dvěma způsoby. Oba zachovávají konstruktor soukromým a poskytují veřejného statického člena umož�ujícího klientům přistupovat k oné jediné instanci dané třídy. V jednom z přístupů je daný veřejný statický člen finálním (konstantním) atributem:

// Jedináček s finálním atributem
public class Elvis {
	public static final Elvis INSTANCE = new Elvis();

	private Elvis() {
		. . .
	}

	. . . // Zbytek vynechán
}

Tento soukromý konstruktor se volá pouze jednou, aby se inicializoval veřejný statický finální atribut Elvis.INSTANCE. Tím, že tu chybějí veřejné nebo chráněné konstruktory, je zaručen "jedno-elvistický" vesmír: po inicializaci třídy Elvis bude existovat přesně jedna instance Elvis - o nic víc a o nic méně. Klient nemůže udělat vůbec nic, aby to změnil.

V druhém přístupu se poskytuje veřejná statická tovární metoda namísto veřejného statického finálního atributu:

// Jedináček se statickou továrnou
public class Elvis {
	private static final Elvis INSTANCE = new Elvis();

	private Elvis() {
		. . .
	}

	public static Elvis getInstance() {
		return INSTANCE;
	}

	. . . // Zbytek vynechán
}

Všechna volání statické metody Elvis.getInstance() vracejí odkaz na stejný objekt a žádná jiná instance Elvis se nikdy nevytvoří.

Hlavní výhodou prvního přístupu je to, že deklarace členů tvořících danou třídu jasně nazančuje, že tato třída je jedináček: veřejný statický atribut je finální, takže tento atribut bude vždy obsahovat odkaz na stejný objekt. První přístup může být také trochu výkonnější, ale dobrá implementace JVM by měla být schopna eliminovat jej úpravou voláním statické tovární metody ve druhém přístupu.

Hlavní výhodou druhého přístupu je možnost bez změny API určit, že daná třída nebude jedináček. Statická tovární metoda jedináčka vrací jedinou instanci dané třídy, což lze ale snadno změnit tak, aby vracela řekněme jednoznačnou instanci pro každé vlákno (tok), které tuto metodu zavolá.

Z toho tedy vyplývá, že je vhodné používat první metodu, když jste si absolutně jisti, že daná třída zůstane navždy jedináčkem. Druhý přístup použijte, chcete-li mít možnost svůj názor změnit.

Aby byla třída jedináčka srializovatelná, nestačí jen do její deklarace přidat implements Serialzable. K zachování vlastnosti jedináčka také musíte poskytnout metodu readResolve (rada 57). Jina každá deserialzace instance skončí vytvořením nové instance, což by v našem případě vedlo dalším výskytům objektu Elvis. Chcete-li tomu zabránit, přidejte do třídy Elvis následující metodu readResolve:

// Metoda readResolve zajišťující vlastnost jedináčka
private Object readResolve() throws ObjectStreamException {

	/** Vrátit jediného opravdového Elvise a nechat nástroj
	 * uvol�ování paměti, ať se postará o podvodníka */
	return INSTANCE;
}

Tuto radu a radu 21, která popisuje vzor typově zabezpečeného výčtu, spojuje společné téma. V obou případech se používají soukromé konstruktory ve spojení s veřejnými statickými členy, čímž se zajišťuje, že nedojde k vytvoření žádných nových instancí po inicializování odpovídající třídy. V případě této rady se vytváří pouze jediná instance dané třídy; v radě 21 se vytváří jedna instance pro každého člena výčtového typu. V následující radě (rada 3) je tento přístup posunut ještě o krok dále: chybějící veřejný konstruktor slouží k zajíštění toho, že nikdy nebudou vytvořeny instance určité třídy.