Translate »
EnglishGermanPolish
EnglishGermanPolish

Zastępujemy często używane idiomy z Google Guava

Dziś na tapetę trafia klasa Preconditions, która zawiera metody pozwalające na zastąpienie popularnych idiomów i konstrukcji służących, najczęściej, do sprawdzania poprawności parametrów.

Po co używać klasy Preconditions?

W ogólności klasa ta dostarcza metod pozwalających na zamianę konstrukcji typu:

Listing 1. Przykładowy kod bez użycia Preconditions

public void method(String param1, Integer param2) {                    
	if (param1 == null)                                                    
		throw new NullPointerException("Param1 can not be null");            
	if (param2 == null)                                                    
		throw new NullPointerException("Param2 can not be null");            
	if (param1.isEmpty())                                                 
		throw new IllegalArgumentException("Param 1 can not be empty");      
	if (param2 > 0)                                                       
		throw new IllegalArgumentException("Param 2 can not be <=0");        
                                                                       
	// rest of code                                                       
}                                                                      

na pozbawiony if-ów czytelniejszy kod.

Listing 2. Przykładowy kod używający Preconditions

public void method(String param1, Integer param2) {                            
	checkNotNull(param1, "Param1 can not be null");                 
	checkNotNull(param2, "Param2 can not be null");                 
	checkArgument(!param1.isEmpty(), "Param 1 can not be empty");   
	checkArgument(param2 > 0, "Param 2 can not be <=0");            
                                                                               
	// rest of code                                                               
}                                                                                                                                                    

Istotna jest tutaj odpowiednia motywacja do takiej zmiany. Ponieważ nieprawidłowo zastosowana klasa może nieźle namieszać.

Preconditions to nie walidator

Kluczowym elementem do prawidłowego zastosowania metod z klasy jest zrozumienie na jakiej zasadzie działają walidatory. Wiele osób popełnia pewien dość prosty, ale bardzo trudny do naprawienia na późniejszym etapie rozwoju aplikacji, błąd. Otóż zapamiętajcie sobie raz na zawsze

WALIDATOR W PRZYPADKU WPROWADZENIA NIEPOPRAWNYCH DANYCH NIE MA PRAWA RZUCAĆ WYJĄTKIEM

Dlaczego? Wyjątek jak sama nazwa wskazuje służy do informowania o sytuacji wyjątkowej, takiej której nie można przewidzieć w momencie pisania kodu albo nie można stworzyć prawidłowego i uniwersalnego sposobu obsługi takiej sytuacji. Wpisanie niepoprawnych danych przez użytkownika nie należy do takiej grupy. To, że ktoś podał np. kod pocztowy bez myślnika nie jest niczym wyjątkowym, niczym czego nie możemy obsłużyć czy przewidzieć.

Metody z klasy Preconditions wyrzucają wyjątki i nie nadają się do walidacji obiektów. Do czego zatem służą? Otóż służą do sprawdzenia stanu obiektu tam gdzie zazwyczaj używamy połączenia IF i THROW. Dobrym przykładem może być tu kontroler, do którego powinny trafić dane po przeciągnięciu przez walidator w wyższej warstwie. Nie możemy jednak założyć w kontrolerze, że dane te nie trafią do nas w jakiś "magiczny" sposób. Zatem wywołanie dodatkowego sprawdzenia już ze zwróceniem wyjątku jest ekwiwalentem stwierdzenia "koleś ale ty chyba zbłądziłeś z tą taczką kupy".

Krótko o JSR-305

Podobną rolę mogą pełnić adnotacje zebrane w ramach JSR-305. Jest tu jednak pewna ważna różnica. Adnotacje powstały z myślą o statycznej analizie kodu pod kątem błędów. Ich dodatkowe zastosowanie jako sposobu na sprawdzenie poprawności parametrów jest fajne lecz wymaga trochę więcej dodatkowej pracy. Generalnie możemy użyć implementacji Bean Validator 1.1 w postaci Hibernate Validator 5.x lecz jest to upierdliwe jeżeli nie dysponujemy kontenerem DI.

Po tym wstępie tradycyjny przegląd możliwości klasy.

Klasa Preconditions

Metody występują zazwyczaj w trzech wersjach. W pierwszej przyjmują tylko obiekt albo warunek sprawdzany. W drugiej poza nim przyjmują też obiekt wiadomości. W trzeciej jako drugi argument jest przyjmowany String zawierający flagi formatowanie, a kolejne argumenty to wartości do podstawienia do tego napisu. Jest to równoważne użyciu String.format.

Metoda checkArgument

Metoda ta przyjmuje jako pierwszy parametr wartość logiczną. Jeżeli będzie ona false to rzuca wyjątek IllegalArgumentException.

Metoda checkElementIndex

Metoda ta jako pierwsze dwa argumenty przyjmuje indeks obiektu w tablicy, liście, napisie oraz długość tej tablicy, listy, napisu. W przypadku gdy pierwszy z argumentów jest większy bądź równy od drugiego rzuca IndexOutOfBoundsException. Jeżeli drugi argument jest negatywny rzuca IllegalArgumentException. Metoda nie posiada wersji z listą argumentów do formatowania wiadomości.

Metoda checkNotNull

Jeżeli przekazana referencja jest null metoda rzuca NPE.

Metoda checkPositionIndex

Bardzo podobna do wcześniejszej checkElementIndex z tą różnicą, że w tym przypadku pierwszy argument może być równy drugiemu. Zatem metoda sprawdza czy pierwszy argument należy do zbioru [0,drugi argument]. Rzuca IndexOutOfBoundsException.

Metoda checkPositionIndexs

Metoda ta sprawdza czy wartość przekazana w pierwszym argumencie należy do zbioru [drugi argument, trzeci argument]. Jeżeli nie rzuca IndexOutOfBoundsException.

Metoda checkState

Jako argument przyjmuje wartość logiczną. Jeżeli wartość ja jest fałszywa to rzuca IllegalStateException.

Kluczowa sprawa - testy

Stworzenie kodu, w którym zastąpimy własne IF-y wywołaniami metod i mówiąc najprościej IF-ami bibliotecznymi nie zwalnia nas z obowiązku napisania testów. Możemy jednak ominąć testy związane z naszymi wywołaniami (ograniczyć się do sprawdzenia czy metody są wołane) powodującymi błąd. Magia tego rozwiązania polega na wypchnięciu odpowiedzialności za przetestowanie prawidłowego działania metody do autorów biblioteki. Dla nas ważne jest tylko sprawdzenie czy podajemy prawidłowe parametry.

W ten sposób udało nam się stworzyć troszkę lepszy kod. Każdy nienapisany IF to maleńkie zwycięstwo w walce o jakość... yyy... ale to zabrzmiało...

3 Responses to “Zastępujemy często używane idiomy z Google Guava”

  1. Michał Mech Says:

    Większość (wszystkie?) moich serwisów zaczyna się od checkNotNull/checkArgument. Dla samej klasy Preconditions (Optional i kolekcji) warto zaopatrzyć się w Guavę. Resztę magii odkryjemy z czasem.

  2. Andrzej Says:

    Ten JSR-305 jest trochę słaby, bo taki interfejs Function z guavy ma ustawiony parametr jako @Nullable i nawet jeśli nasza implementacja nigdy nie dostanie nulla (przez co olewamy sprawdzanie parametru, czy jest nullem), to statyczna analiza i tak pokazuje to jako błędny kod… Dodanie @NonNull na implementacji oczywiście nic nie daje…

  3. Koziolek Says:

    Uroki analizy statycznej w przypadku “oderwanych od rzeczywistości” elementów, których wywołanie następuje gdzieś głęboko z trzewi jakiejś zewnętrznej biblioteki.

Leave a Reply