Przejdź do głównej zawartości

Immutable collections - Java

Mierzenie się z kolekcjami i poprawnym ich wykorzystaniem jest problematyczne dla początkujących programistów. Jak często widzieliście w jakimś kodzie fragment typu:
cart.getProducts().add(product);
Sam pisałem niegdyś takie brzydkie rzeczy. W czasie, w którym wyrabiałem sobie moje skromne doświadczenie wypracowałem sobie jednak pewne zasady jak postępować z kolekcjami. Ktoś mógłby się ze mną spierać - nawet podając sensowne argumenty ale w mojej pracy trzymanie się tych nawyków sprawdza się i na razie się ich trzymam.

 W czym problem?

Przedstawiony wyżej fragment kodu to wyraźne złamanie zasady enkapsulacji. Skoro obiekt udostępnia swoje wnętrze (w typ przypadku jakąś kolekcję) i można z tym robić wszystko co się nam spodoba to po co te wszystkie modyfikatory dostępu i po co w ogóle taki obiekt? Skoro obiekt, który posiada jako swoją właściwość (property) jakąś listę i udostępnia ją tak po prostu na zewnątrz to może okazać się, że biedny obiekcik jest nieświadomy, że ktoś mu grzebie w brzuchu 😨.

Co począć?

Immutable collections! Coś co można by powiedzieć, że istnieje w Java od wieków bo od wersji 1.2, jednak jest to rzecz nieco ukryta przed niedoświadczonymi programistami. Kolekcja niemutowalna to taka, która co prawda posiada dane i zapewnia do nich dostęp ale nie pozwala ich dodawać, ani usuwać. Podczas próby wykonania takiej operacji jest rzucany wyjątek UnsupportedOperationException. Jak tworzyć takie kolekcje? W Java od 1.2 do 1.8 wystarczy skorzystać z klasy narzędziowej java.util.Collections i jej metod statycznych unmodifiable.... Sposób użycia:
List<String> strings;
List<String> unmodifiableStrings = Collections.unmodifiableList(strings); 

No i co ja z tego mam?

Wyobraź sobie sytuację, że piszesz sobie aplikację na zaliczenie ze swoim kolegą Ryśkiem. Napisałeś piękną klasę, przetestowaną i elegancką. Okazuje się jednak, że aplikacja działa jakoś dziwnie. Brakuje jakichś danych, a czasem są w złej kolejności. Spojrzałeś w kod Ryśka. Okazało się, że potrzebował wpisów z listy, znajdującej się w Twojej klasie i nie zrobił własnej kopii tylko operował na Twojej liście.
Korzystanie z immutable collections wymusza tworzenie kopii kolekcji! Gdybyś udostępnił własną kolekcję w postaci niemutowalnej to nikt nie miałby prawa nic namieszać.

No ale to tyle dodatkowego kodu...

No ejj... wcale nie tyle. tylko kilka znaków w linii więcej. Niestety rzeczywistość javova tak w tej chwili wygląda, że wielu udogodnień brakuje w samym języku a dostępne są zazwyczaj w postaci rozrastającego się ciągle API. W innych nowoczesnych językach takie struktury są zapewniane w inny sposób. Np. w pythonie można myśleć o krotce (tuple)  jako o liście niemodyfikowalnej. Język Kotlin z kolei zapewnia, że każda kolekcja jest domyślnie immutable o ile programista jawnie nie określi mutowalnego typu kolekcji.
Można również wykorzystać jakąś z bibliotek jak np. Guava, która dostarcza zestaw typów immutable, którymi można się posługiwać zamiast typowych kolekcji z API języka.

To kiedy stosować jaką kolekcję?

Odpowiedź jest prosta. Jeśli to tylko możliwe to zawsze stosuj kolekcje niemutowalne!
Ja stosuję 3 proste zasady:
  1. Każdy getter zawsze zwraca kolekcję immutable
  2. Przy przekazywaniu kolekcji do metody innego obiektu przzekazuję kolekcję immutable
  3. Przy otrzymywaniu kolekcji jako parametr zawsze kopiuję kolekcję. Zabezpieczam się przed sytuacją gdy ktoś przekazał właśnie kolekcję immutable, a zarazem nie psuję mu życia jeśli tego nie zrobił.

Co za głupoty...

Zdaję sobie sprawę, że to co wyżej napisałem może być dla wielu czytelników oczywiste, inni mogą się nie zgodzić z niektórymi słowami. Mam jednak nadzieję, że ten tekst trafi do początkujących i skłoni ich do chwili zastanowienia. Pozdro 😊

Komentarze

Popularne posty z tego bloga

Aplikacja czasu rzeczywistego w Spring i AngularJS

Naszła mnie potrzeba aby utworzyć pewien dashboard, który reagowałby na zmiany przychodzące z serwera. Jako, że tworzę swoje aplikacje z wykorzystaniem Spring Boot (backend) i AngularJS (frontend) to o komunikacje serwer -> klient należy zadbać samemu. Po pospiesznej analizie tematu okazało się, że taką komunikację zapewnia protokół WebSockets (co dla niektórych może jest oczywiste, lecz dla mnie nie było). Co trzeba zrobić? Zestawić połączenie pomiędzy serwerem, a klientem i przesyłać wiadomości. Proste? Proste. No to jazda.
Projekt Kombinacja Spring Boot i AngularJS (angular w wersji 1) tak mi się spodobała, że utworzyłem sobie własny archetyp mavena, aby szybko móc tworzyć kolejne proste aplikacje. Jest on dostępny na moim GitHubie. Instrukcje tworzenia z niego projektu są w Readme, nie będę się powtarzał.
Eventy Taka aktualizacja danych musi być wywoływana przez jakieś zdarzenie. Na potrzeby przykładu wykorzystałem springowe eventy. Eventem w springu może być zwykłe POJO. W moi…

Dlaczego default jest blee... Wielodziedziczenie

Piszę ten post gdyż złapałem się na tym, że nie potrafiłem uargumentować swojego przekonania dotyczącego pewnego aspektu programowania gdy padło pytanie: "dlaczego?". Dopiero później wszystko sobie przypomniałem i mam nadzieję, że ten post nie pozwoli mi już o tym zapomnieć ;).

Dawno, dawno temu, gdy ukazała się Java w wersji 8, wielu z nas zachwycało się nowym mechanizmem: metody default! Bo to takie fajne, nowe, można ograniczyć powtarzanie się kodu, no generalnie bajer. Dopiero po chwili niestety, dotarło, że wprowadzenie takiego rozwiązania to cofanie się w czasie.
 Wielodziedziczenie Podczas lat rozwoju języków programowania pojawił się w pewnym momencie trend aby unikać stosowania rozwiązania zwanego wielodziedziczeniem. Czym jest ten mechanizm? Pozwala on na dziedziczenie z wielu klas bazowych jednocześnie. Unikanie jego stosowania, a nawet unikanie implementacji takiego mechanizmu w nowoczesnych językach jest poparte sensownymi argumentami.

Po pierwsze, może wydarzy…