30 lipca 2009

Efektywny programista Java, część 1 - singletony

Zgodnie z zapowiedzią w poprzednim poście, dzisiaj początek cyklu postów z najciekawszymi poradami z książki "Java. Efektywne programowanie". W pierwszej kolejności postaram się przedstawić te tematy, z którymi programista Java może zetknąć się najczęściej.

Trzy sposoby na tworzenie singletonów


Najczęściej singleton w Javie wygląda następująco:


package pl.tdziurko.effectivejava;

public class Boniek {

public static final Boniek INSTANCE = new Boniek();

private Boniek() {
// stwórz jedyną instancję Zbigniewa Bońka
}

public void doMagicWithBall() {
// dokonaj czegoś niesamowitego na boisku
}

}


W powyższym przykładzie widać od razu, że jest to singleton: prywatny konstruktor i statyczne pole finalne z jedyną instancją klasy dają o tym wystarczająco jasny sygnał.

Drugi sposób przedstawiony w książce jest bardzo podobny:


public class Boniek {

private static final Boniek INSTANCE = new Boniek();

private Boniek() {
// stwórz jedyną instancję Zbigniewa Bońka
}

public Boniek getInstance() {
return INSTANCE;
}

public void doMagicWithBall() {
// dokonaj czegoś niesamowitego na boisku
}
}


Stosując powyższe podejście mamy większą elastyczność przy późniejszych modyfikacjach naszej klasy. Możemy bez problemu (czyli bez widocznych zmian w zewnętrznym wyglądzie klasy) zmienić implementację metody getInstance(), tak aby zwracała nie jedyną instancję Zbigniewa Bońka, ale na przykład jedną instancję dla danego wątku czy danego klienta.

Oba podejścia nie są niestety odporne na refleksję, nie są również przystosowane do serializacji. Jeśli chcemy, aby nasz singleton po serializacji i deserializacji był ciągle singletonem poza dodaniem implements Serializable musimy przeładować metodę readResolve() tak, aby zwracała obiekt INSTANCE.
Z kolei przed refleksją można się uchronić dodając blok w konstruktorze, który rzuca wyjątek w momencie, gdy istnieje już instancja obiektu tej klasy.

Ostatnim (i najbardziej polecanym przez autora książki) sposobem na tworzenie singletonu jest zastosowanie typu wyliczeniowego:


public enum Boniek {

INSTANCE;

void doMagicWithBall() {
// dokonaj czegoś niesamowitego na boisku
}
}


Dzięki zastosowaniu typu wyliczeniowego mamy za jednym zamachem załatwiony problem singletonu, bezpieczeństwa serializacji i zabezpieczenie przed użyciem refleksji. Wszystko zapewnia sam język i nie musimy się o to martwić.

Osobiście najczęściej stosowałem podejście z polem private final static i metodą getInstance(), ale myślę, że zastosowanie typu wyliczeniowego jest bardzo interesującą alternatywą, którą warto dodać do swojego repertuaru :)

Brak komentarzy:

Prześlij komentarz