Przejdź do głównej zawartości

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ć się problematyczna sytuacja, w której część z klas bazowych zawiera implementację tej samej metody o dokładnie tej samej definicji. Która w takim razie ma być zastosowana? Oczywiście są pewne domyślne zachowania zależne od języka oraz można zwykle określić, z której należy korzystać ale czy nie wprowadza to tylko zbędnego zamieszania?

Po drugie, zastanawiam się kiedy ostatnio potrzebowałem takiego mechanizmu? Nie przypominam sobie ani jednej sytuacji, w której byłoby mi potrzebne dziedziczenie z kilku klas. Uważam, że każdy problem jestem w stanie rozwiązać w inny sposób.

Po trzecie, stosowanie wielodziedziczenia jest sprzeczne z ideą stawiania na kompozycję ponad dziedziczenie, której jestem wielkim fanem. O tym napisano baaardzo wiele i tutaj nie dam się przekonać ;).

Ponadto jestem skłonny stwierdzić, że takie rozwiązanie nie służy pisaniu czystego, zarządzalnego kodu. Po prostu.

Wielodziedziczenie, a interfejsy

No dobra, a dlaczego w takim razie z interfejsami jest wszystko ok? Otóż dlatego, że interfejs tylko definiuje funkcjonalność, a klasa ją implementuje. Nie ma w tym wypadku miejsca na żadnego rodzaju niejasności.

Wracając do tematu

Miałem pisać przecież o metodach default, a cały tekst jest o wielodziedziczeniu. Okazuje się bowiem, że developerzy Oracle'a wprowadzili nam cichaczem do języka mechanizm wielodziedziczenia pod nazwą metod default. Bo jak inaczej nazwać to, że interfejs zyskuje możliwość implementacji metody?
Wprowadzenie mechanizmu metod default jest uargumentowane potrzebą rozbudowy API Javy (chociażby biblioteki streamów) i jestem to w stanie zaakceptować ale jestem zdania, że trzeba z niego korzystać ostrożnie i wyłącznie w przypadkach niezbędnych (jak np. chęć rozbudowy biblioteki bez wpływu na działanie korzystającego z niej kodu).

Komentarze

Popularne posty z tego bloga

Sneaky throws, czyli checked i unchecked exception w jednym!

Często podczas pisania kodu Java korzystam z wyrażeń lambda wprowadzonych wraz z Java 8. Często prowadzi to też do pewnych nowych problemów i zmusza do szukania rozwiązań. Przyjrzyjmy się jednemu z nich. Checked exception w wyrażeniu lambda Często zdarza się, że metoda wywoływana wewnątrz lambdy rzuca wyjątek. Nie ma sprawy gdy wyjątek jest obiektem klasy będącej podklasą RuntimeException . Wtedy po prostu się nim nie przejmujemy. Wyjątek jest przekazywany w górę stosu wywołań. Problem zaczyna się gdy kompilator zmusza nas do obsługi wyjątku. Jak zwykle mamy 2 wyjścia: obsłużyć wyjątek za pomocą bloku try-catch , bądź zadeklarować przekazanie wyjątku dalej za pomocą słowa kluczowego throws . O ile wiemy co zrobić po złapaniu wyjątku to wszystko gra. Co natomiast gdy chcemy wybrać drugą opcję? Większość interfejsów stosowanych jako typy parametrów, często przekazywanych jako lambdy jak np: Function , Consumer czy Supplier nie deklarują, że mogą rzucić wyjątek. W takim wypadku druga

Wzorzec Open Session in View w Spring Boot

Dzisiejszy post będzie z cyklu: "Wtf? Dlaczego to działa?". A dotyczy on pewnego wzorca, którego implementacja jak się okazuje jest we frameworku Spring Boot domyślnie włączona, czego nie wszyscy programiści mogą się spodziewać. Geneza Zaczęło się od tego, że pisałem kolejny test integracyjny do kolejnego kontrolera. Test napisany, uruchamiam, zielono. No i przeglądam sobie jeszcze raz kod zanim wypchnę zmiany do review. I rzuciło mi się w oczy, że zapomniałem dodać adnotacji @Transactional (do tej pory uważałem, że to jest zawsze wymagane). I wtedy zadałem sobie przytoczone na wstępie pytanie. Dlaczego test przeszedł skoro mój serwis dociąga sobie obiekt oznaczony jako LAZY ? Dużo się nagimnastykowałem by wpisać odpowiednie query do Google'a i znaleźć odpowiedź: Open Session in View. Ale o tym za chwilę. Jak działa Hibernate Żeby lepiej zrozumieć opisywane zagadnienie warto przypomnieć jakie kroki w uproszczeniu musi wykonać Hibernate podczas komunikacji z bazą d

Pułapki logowania w Javie

Czy spotkałeś kiedyś się z kodem takim jak poniższy? if (logger.isDebugEnabled()) { logger.debug("Request: " + requestObject); } Ja tak. Jakiś czas temu napotkałem na mnóstwo takich fragmentów w kodzie, który analizowałem. Zastanowiło mnie po co została wykorzystana tutaj instrukcja warunkowa - a jako, że if-y uwazam za zło konieczne postanowiłem to zbadać. Czy nie do tego właśnie służą biblioteki do logowania i udostępniane przez nie metody jak debug żeby właśnie sterowane konfiguracją decydowały co zalogować a co nie? Co więcej, okazało się, że instrukcję warunkową wprowadził ktoś w ramach większej akcji. I jak się okazuje, prawdopodobnie znacznie zwiększył dzieki temu wydajność aplikacji. W dzisiejszym wpisie postaram się wyjaśnić jak korzystać z loggera aby nie zaszkodziło to wydajności. Po co if-y? Na początek wyjaśnić trzeba czemu ten if tak na prawdę służy. Korzystamy z biblioteki do logowania i spodziewamy się, że wykorzystujac wybraną metodę, zostanie