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ć - choćby 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ą adekwatność (property) jakąś listę i udostępnia ją tak po prostu na zewnątrz to może okazać się, iż biedny obiekcik jest nieświadomy, iż ktoś mu grzebie w brzuchu 😨.Co począć?
Immutable collections! Coś co można by powiedzieć, iż 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ę, iż piszesz sobie aplikację na zaliczenie ze swoim kolegą Ryśkiem. Napisałeś piękną klasę, przetestowaną i elegancką. Okazuje się jednak, iż 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ę, iż 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, iż wielu udogodnień brakuje w samym języku a dostępne są zwykle 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, iż 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żeli to tylko możliwe to zawsze stosuj kolekcje niemutowalne!Ja stosuję 3 proste zasady:
- Każdy getter zawsze zwraca kolekcję immutable
- Przy przekazywaniu kolekcji do metody innego obiektu przekazuję kolekcję immutable
- 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żeli tego nie zrobił.