6 sierpnia 2009

Efektywny programista Java, część 3 i 4 - klasy użytkowe i unikanie zbędnego powielania obiektów

Dzisiaj kolejna porcja porad jak efektywnie programować w Javie. Tym razem w jednym poście dwie porady, bo każda z nich jest zbyt krótka, by umieszczać je oddzielnie.

Klasy użytkowe

Prawie w każdym projekcie pojawia się przynajmniej jedna klasa, która gromadzi metody użytkowe wspomagające działanie aplikacji. Taką klasę nazywamy najczęściej klasą pomocniczą (ang. utility class).
Składa się ona z kilku/kilkunastu metod statycznych używanych w różnych miejscach aplikacji. Taka klasa nie powinna umożliwiać tworzenia swojej instancji, gdyż jest to zbędne.

Bardzo łatwo możemy wymusić takie zachowanie na innych programistach tworząc konstruktor prywatny:


public class Utils {

/*
* This is utility class, you don't have to create instance to use it
*/
private Utils() { }

public static String prepareUserStringForEmail(User user) {
// ...
}

public static String createRandomString(int length) {
// ...
}

public static String formatDateWithTime(Date date) {
// ...
}
}



Dzięki takiemu prostemu zabiegowi, nasz kod zyskał na czytelności, a inni programiście nie będą używać naszej klasy w sposób niepoprawny.

Optymalizowanie tworzenia obiektów, których używamy wielokrotnie

Przyjrzyjmy się poniższemu fragmentowi pewnej klasy:


public class Utils {

/*
* This is utility class, you don't have to create instance to use it
*/
private Utils() { }

public static boolean isOlderThanCodeHardGoProBlogOwner(Date birthDate) {
if(birthDate == null) {
throw new IllegalArgumentException("Date can not be null");
}

Calendar calendar = Calendar.getInstance();
calendar.set(1984, Calendar.JULY, 22);
Date codeHardGoProOwnerDateOfBirth = calendar.getTime();

return birthDate.compareTo(codeHardGoProOwnerDateOfBirth) < 0;
}

}

Sprawdzamy tutaj, czy czyjaś data urodzin jest wcześniejsza niż moja. Tworzymy obiekt klasy Calendar, ustawiamy mu odpowiednie wartości roku, miesiąca i dnia, a następnie porównujemy ze zmienną birthDate.
Gdy wywołamy metodę kolejny raz, obiekt calendar zostanie utworzony ponownie i zostanie mu ustawiona ta sama data co poprzednio. Nie jest to rozwiązanie najbardziej optymalne. Widać tutaj wyraźnie, że tworzenie obiektu calendar i ustawianie mu odpowiedniej wartości powinno zostać wydzielone poza metodę i wykonywane tylko raz. Efekt taki możemy uzyskać przenosząc fragment kodu do bloku statycznego, a zmienną klasy Date uczynić prywatną i statyczną:


public class Utils {

private final static Date CODE_HARD_GO_PRO_BLOG_OWNER_BIRTH_DATE;

static {
Calendar calendar = Calendar.getInstance();
calendar.set(1984, Calendar.JULY, 22);
CODE_HARD_GO_PRO_BLOG_OWNER_BIRTH_DATE = calendar.getTime();
}

private Utils() { }

public static boolean isOlderThanCodeHardGoProBlogOwner(Date birthDate) {
if(birthDate == null) {
throw new IllegalArgumentException("Date can not be null");
}

return birthDate.compareTo(CODE_HARD_GO_PRO_BLOG_OWNER_BIRTH_DATE) < 0;
}

}


Teraz nasza metoda będzie wykonywała się trochę szybciej. Oczywiście zysk będzie znacznie większy, gdy taki wielokrotnie używany obiekt będzie bardziej skomplikowany, a jego tworzenie kosztowniejsze i bardziej czasochłonne.