Menu Zamknij

Polimorfizm w Javie, to Pożegnanie z Ifologią!

Polimorfizm w Java vs If
Gdy twój zespól wprowadza nowe założenia biznesowe

Czy miewasz czasem tak, że niektóre zmiany w projekcie, wydają ci się bardzo ryzykowne? Może czujesz lekkie poirytowanie, gdy non stop zmieniają się założenia? Albo boisz się tego, że główna funkcjonalność twojego produktu, może w każdej chwili przestać działać? Jeżeli odpowiedź na którekolwiek z tych pytań, jest twierdząca, to najwyższy czas, by wprowadzić polimorfizm.

Problem z Ifologią

Jeżeli nie stosujesz polimorfizmu, to na pewno dobrze znasz ten problem. Problem, w którym mnogość typów oraz funkcji, powoduje takie zależności, które są bardzo trudne w analizie. Dodatkowo ich obsługa w systemie jest porozrzucana jak fragmenty petard po sylwestrze.

Taki stan rzeczy może, powodować, że nawet najdrobniejsza zmiana, przynosi niespodziewane błędy. Pojawia się wiele elementów, o których trzeba cały czas pamiętać, by utrzymać spójność systemu. Rzeczy wydawałoby się całkowicie od siebie niezależne, nagle wpływają na siebie w trudny do wyjaśnienia sposób.

Mała zmiana założeń potrafi wtedy generować naprawdę dużo problemów z jej wprowadzeniem. Tak właśnie kończy się, gdy zaufasz instrukcji if za bardzo.

Polimorfizm w Java vs If
Twój proces biznesowy, gdy zapomniałeś poprawić swoich if

Gdy jawnie wykorzystujesz dane reprezentujące stan obiektów, tylko po to, by podjąć decyzje o przepływie procesu. To całość staje się mocno niestabilna i wrażliwa na najdrobniejsze zmiany. Utrzymanie takiego kodu jest uciążliwe, a sterowanie procesem w ten sposób jest po prostu niebezpieczne.

Przyrównywanie non stop cech takich jak type, status, state do swoich potencjalnych wartości to koszmar. W przypadku, gdy takich miejsc jest dużo, próba wprowadzenia nowych wartości, będzie wymagała sporo pracy. Odszukanie tych wszystkich if’ów i upewnienie się, że nowa wartość nie wprowadzi w systemie zamętu to ciężkie analityczne zadanie.

Za Szybki, za Wściekły Programista

Opowiem ci historię pewnego programisty. Powiedzmy Marka. Marek nie miał jeszcze dużego doświadczenia. Pewnego dnia przyszedł do niego ktoś z biznesu i informuje go, że w systemie wymagana jest obsługa nowego typu klienta. To klient typu VIP. Sam proces dla takiego klienta nie różni się znacznie od zwykłego. To tylko taka drobna, mała zmiana.

Nasz programista myśli sobie – Hmm… To nic trudnego. Wystarczy dodać jeden prosty if i wszystko będzie załatwione. Zadanie zrobię najszybciej jak to możliwe i wszyscy będą mnie wtedy podziwiać, jakim jestem szybkim i wydajnym pracownikiem.

Nie rób tak!

Gość nie uświadamia sobie tego, że idzie po linii najmniejszego oporu. Właśnie obrał kurs ku przepaści, a razem z nim idzie tam jego zespół i cały projekt. Być może koledzy teraz mu przyklaskują, ale za pół roku będą pierwsi wskakiwać na szalupę ratunkową w kierunku Nowa Praca.

Dodanie jednego prostego if jest zawsze łatwe, ale to polimorfizm jest prosty w użyciu. Owszem bywa trudny do wprowadzenia, ale jak już jest i jest dobrze zaprojektowany, to praca z nim jest lekka i przyjemna.

Kontynuując naszą historię, wyobraź sobie, że do Marka po tygodniu wraca tamta osoba i mówi, że przemyślała sprawę i teraz klienci VIP będą mieli dodatkowe przywileje. Dla Marka to kolejny wyłom w procesie. Kolejne miejsca, gdzie będzie sprawdzał, czy klient jest VIP-em.

Polimorfizm w Java vs If
Marek kontra zmiana założeń biznesowych

Dopóki takich miejsc jest mało, to idzie z tym jakoś żyć, ale kierunek jest już jasny. Marek zmierza z projektem ku zagładzie. Kolejne modyfikacje będą wymagać coraz to więcej czasu.

W pewnej chwili Marek może już, nawet czuć, że polimorfizm byłby dużo lepszy. Jednak mając do wyboru refaktoryzację wrażliwej części systemu, a pomęczenia się trochę z kolejnymi if’ami – Jak myślisz, co wybierze?

Polimorfizm to Wygoda

Polimorfizm jest po prostu wygodny. Pozwala punktowo uderzać ze zmianami, zamiast rozpraszać się po całym systemie. Wtedy modyfikacja specyficznych funkcjonalności jest prosta – Namierzamy klasę odpowiedniego typu i tylko ją modyfikujemy. Nie musimy już skakać od klasy do klasy i modyfikować warunki w całym procesie.

Polimorfizm to prosta koncepcja. Traktujemy w jednolity sposób rzeczy, które się między sobą różnią, ale ich rola jest podobna. Jeżeli chcemy dodać nowy typ, to wystarczy nowa implementacja. Warunkiem jest tylko to by nasze obiekty spełniały zasadę podstawienia Liskov. Jakie to proste, a ile wnosi elastyczności do projektu.

Dużo łatwiej jest dostarczyć obiekt dopasowany do procesu, niż dostosowywać ciągle proces do nowych wartości.

Polimorfizm jest również wygodny w analizie. Dużo łatwiej wychwycić, że psujemy proces, gdy mamy kontrakt w postaci interfejsu. Bardzo szybko zauważymy, że kontrakt został zerwany, gdy tylko kod zaświeci się na czerwono. Natomiast jeżeli zamiast używać polimorfizmu, przetwarzamy prymitywne wartości, to dużo trudnej dostrzec taką sytuację.

Pamiętaj o tym, by to proces wyznaczał polimorfizm, a nie samo podobieństwo obiektów. Nie ma sensu robienia polimorfizmu, dla zasady. Robienie go z błahych pobudek prawdopodobnie skończy się łamaniem zasady segregacji interfejsów. Polimorfizm przydaje się, tylko wtedy gdy ujednolicenie pracy z obiektami daje wartość w postaci większej elastyczności, stabilności systemu lub zmniejszeniu zależności.

Jak Szybko, Łatwo i Przyjemnie uzyskać Polimorfizm?

Daj sobie chwilkę, na przeanalizowanie procesu. Sprawdź, jak wartości oddziałują na proces. Zobacz, na które funkcjonalności wpływają najmocniej. Przeanalizuj, skąd się one biorą, kiedy się zmieniają. Zobacz, w jakich obiektach są przechowywane.

Jak już się trochę odnajdziesz w sytuacji, to łatwiej będzie zaprojektować dobre polimorficzne rozwiązanie. Teraz pomyśl nad zakresem, który twój polimorfizm powinien obejmować. Czy wygodniej będzie zmienić wartości sterujące procesem w pełnoprawne obiekty, czy raczej polimorfizmowi powinno podlegać to, co je przechowuje. Chodzi o wybranie poziomu abstrakcji, który da najwyższy zwrot z inwestycji.

Kolejny krok to zdefiniowanie abstrakcyjnych zachowań, które mogłyby stać się częścią kontraktu. Zachowania, takie łatwo zauważyć, patrząc na całość od strony procesu. Prawdziwą sztuką będzie dobranie ich w taki sposób, by konkretny rodzaj samego obiektu był dla samego procesu nieistotny.

Polimorfizm w Javie
Czy widzisz, żeby kazano szczekać, miauczeć i kwakać ?

Jeżeli mamy już dobrze zaprojektowany interfejs. Wyodrębniamy odpowiednie typy. Dodajemy implementację dla każdego wyodrębnionego przez nas typu. Lokalizujemy miejsce, gdzie wpadają surowe dane i uruchamiamy tam fabrykę, która przetworzy nam surowe wartości w pełnoprawne obiekty.

Po takim zabiegu jeszcze wiele może ci się wyklarować. Może się okazać, że zauważasz inne zachowania, które można uwzględnić w interfejsie. Być może implementacja niektórych będziecie się powtarzać. Można wtedy pokusić się o wprowadzenie klasy abstrakcyjnej.

Pamiętaj tylko, że interfejs nie może zdradzać prawdziwego typu obiektu. Metody takie jak getType nie wchodzą w grę. Podobnie jest z wywoływaniem na obiektach metody instance of. Takie działanie jest szybkim powrotem do ifologii. Zgodnie z zasadą Tell, don’t ask nie powinieneś bezpośrednio korzystać danych obiektu.

Wymówki dla nieużywania Polimorfizmu

Będę i tak nadal używać switch, tylko, że w fabryce

Tak, to prawda. Nadal masz switch, ale jeszcze nie wiesz, jak dużo zyskałeś. Po pierwsze dużo łatwiej namierzyć cie miejsce do realizacji zmiany zachowania dla danego typu. Po drugie możesz łatwo wprowadzać nowe typy, które będą prawidłowo funkcjonować w aktualnym procesie, wystarczy tylko implementować interfejs. Ostania sprawa to lepsza hermetyzacja dany, co uchroni cię przed ich wyciekiem i potrzebą zmian w różnych odległych częściach systemu.

Mam różne parametry wejściowe do funkcji

Do metody wytwarzającej możesz podać takie parametry, które ci się przydadzą. Twoje metody na interfejsie nie muszą przyjmować żadnych parametrów, jeżeli parametry dla metody się aż tak różnią między sobą. Z powodzeniem możesz zastosować wzorzec Polecenie.

Potrzebuje wstrzykiwać springowe zależności

Twoja fabryka może z powodzeniem być springowym beanem, mogą do niej zostać wstrzyknięte inne potrzebne dla różnych typów serwisy. I być odpowiednio wstrzykiwane przez konstruktor konkretnych typów.

Moim obiektem z typem jest hibernetowa encja

Jeżeli twoim obiektem jest encja i potrzebujesz zastosować dla niej polimorfizm, to możesz zrobić to na dwa sposoby, albo przez agregacje encji w dodatkowych obiektach i oddziaływanie na nią zgodnie z typem danych, poprzez metody ustalone w interfejsie. Drugim sposobem jest użycie adnotacji @DiscriminatorColumn oraz @DiscriminatorValue z JPA.

Potrzebuje wyciągać specyficzne dla typu dane

Nie potrzebujesz. Typ jest przeźroczysty. Nie powinien zwracać specyficznych danych. I nie waż się robić rzutowania w dół, bo wtedy cały polimorfizm idzie w piach.

W ostateczności możesz zwrócić Mapę lub kontener heterogeniczny, ale byłbym bardzo ostrożny. Nie używaj tego w logice domenowej. To jeszcze przejdzie na granicach kontekstu, ale dużo lepiej w takich przypadkach spisują się eventy springowe, a te już nie będą wymagały tak chamskiego wyciągania danych.

Dlaczego Polimofizm jest Fajny?

Zrób tę małą rzecz, dodaj interfejs, dodaj fabrykę i poszczególne implementacje dla typu. Zobaczysz, ile zyskasz i jak szybko zwróci się taki, zabieg. Jeżeli jesteś na wczesnym etapie projektu, nie odkładaj tego na później. Im szybciej się tym zajmiesz, tym więcej czasu zaoszczędzisz. Później, sytuacja może już nie być taka atrakcyjna.

Polimorfizm to jeden z fundamentów programowania obiektowego. Zauważ, że duża część wzorców projektowych jest o niego oparta, Strategia, Stan, Kompozyt, Null Object i wiele innych. To nie jest przypadek.

Naprawdę nie trzeba dużo by go osiągnąć. Wystarczy tylko Interfejs, fabryka i kilka implementacji. W prostych przypadkach może to być nawet statyczna metoda wytwórcza, taka jak proponuje w swojej książce Java Efektywne Programowanie Joshua Bloch.

Mam nadzieje, że przekonałem Cię do stosowania Polimorfizmu 🙂

Please follow and like us:
Skuteczna refaktoryzacja w 10 krokach!

Odbierz Darmowy Poradnik o Refaktoryzacji!

Poznaj kilka prostych technik i wprowadź nową jakość w swoim projekcie.

Dzięki za dołączenie do mojej listy.

Coś poszło nie tak :( Spróbuj jeszcze raz.