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.

2 komentarze:

  1. A czemu nie po prostu:

    private final static Date CODE_HARD_GO_PRO_BLOG_OWNER_BIRTH_DATE = new GregorianCalendar(1984, Calendar.JULY, 22).getTime();

    Przenośność?

    OdpowiedzUsuń
  2. Faktycznie Twój sposób wydaje się lepszy niż podany przez autora. Zastanawiam się dlaczego w książce nie zaproponowano właśnie takiego rozwiązania. Być może chodzi właśnie o potencjalną przenośność pomiędzy różnymi kalendarzami (żydowski, muzułmański, itp.), choć to trochę wydumany powód. A może po prostu poprawność polityczna nakazywała jawnie nie faworyzować w książce jednego kalendarza ponad inne? :)

    OdpowiedzUsuń