{"id":30781,"date":"2020-09-30T12:58:04","date_gmt":"2020-09-30T10:58:04","guid":{"rendered":"https:\/\/nearshore-it.eu\/artykuly\/od-kaczek-do-delegacji-czyli-problemy-z-dziedziczeniem-w-javie\/"},"modified":"2024-11-07T13:39:42","modified_gmt":"2024-11-07T12:39:42","slug":"od-kaczek-do-delegacji-czyli-problemy-z-dziedziczeniem-w-javie","status":"publish","type":"post","link":"https:\/\/nearshore-it.eu\/pl\/artykuly\/od-kaczek-do-delegacji-czyli-problemy-z-dziedziczeniem-w-javie\/","title":{"rendered":"Od kaczek do delegacji, czyli problemy z dziedziczeniem w Javie"},"content":{"rendered":"\n<div class=\"table-of-contents\">\n    <p class=\"title\">Przejd\u017a do:<\/p>\n    <ol>\n                    <li><a href=\"#Wstep\">1.  Wst\u0119p<\/a><\/li>\n                    <li><a href=\"#Reguly-pani-Barbary-Liskov\">2.  Regu\u0142y pani Barbary Liskov<\/a><\/li>\n                    <li><a href=\"#Czysta-abstrakcja\">3.  Czysta abstrakcja<\/a><\/li>\n                    <li><a href=\"#Wada-tego-podejscia\">4.  Wada tego podej\u015bcia<\/a><\/li>\n                    <li><a href=\"#Kompozytor-wyjezdza-w-delegacje\">5.  Kompozytor wyje\u017cd\u017ca w delegacj\u0119<\/a><\/li>\n                    <li><a href=\"#Wady-kompozycji\">6.  Wady kompozycji<\/a><\/li>\n                    <li><a href=\"#Kaczki-a-kacze-typowanie\">7.  Kaczki, a kacze typowanie<\/a><\/li>\n                    <li><a href=\"#Lombok-na-ratunek\">8.  Lombok na ratunek<\/a><\/li>\n                    <li><a href=\"#Czas-na-podsumowanie\">9.  Czas na podsumowanie<\/a><\/li>\n            <\/ol>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"Wstep\">Wst\u0119p<\/h2>\n\n\n\n<p>Programuj\u0105c w Javie (a w\u0142a\u015bciwie w ka\u017cdym j\u0119zyku zorientowanym obiektowo), co chwil\u0119 korzystamy z dobrodziejstw tego, co oferuje nam programowanie obiektowe: <strong>klasy, interfejsy, polimor\ufb01zm, dziedziczenie.<\/strong> I mo\u017cna by tak jeszcze d\u0142ugo wymienia\u0107. Szczeg\u00f3lnie ta ostatnia opcja (dziedziczenie) pozwala nam w \u0142atwy spos\u00f3b opisa\u0107 wiele podobnych do siebie byt\u00f3w w prosty spos\u00f3b \u2013 t\u0119 cz\u0119\u015b\u0107, kt\u00f3ra jest dla nich wsp\u00f3lna, deklarujemy tylko raz w klasie nadrz\u0119dnej; natomiast cz\u0119\u015b\u0107 zmienn\u0105 umieszczamy w kodzie klas podrz\u0119dnych po niej dziedzicz\u0105cych.<\/p>\n\n\n\n<p><strong>Okazuje si\u0119 jednak, i\u017c zar\u00f3wno w \u017cyciu codziennym (spadek z d\u0142ugami po dalekim krewnym, o kt\u00f3rym nawet nie mieli\u015bmy poj\u0119cia!), jak i w programowaniu, dziedziczenie mo\u017ce nam przysporzy\u0107 wielu niespodziewanych trudno\u015bci. Na czym polega problem?<\/strong> Aby to wyja\u015bni\u0107, pos\u0142u\u017cymy si\u0119 na pocz\u0105tek rysunkiem odnosz\u0105cym si\u0119 do zasady, o kt\u00f3rej b\u0119dziemy m\u00f3wi\u0107 w dalszej cz\u0119\u015bci artyku\u0142u. Nast\u0119pnie zmienimy rysunek w prost\u0105 aplikacj\u0119 z\u0142o\u017con\u0105 z kilku klas. Sp\u00f3jrzmy na dwa poni\u017csze rodzaje kaczek:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image is-resized\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_jpro_graphic.jpg\" alt=\" class=\" class=\"wp-image-30908\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Rysunek 1: Z\u0142e u\u017cycie dziedziczenia na przyk\u0142adzie kaczek<\/em><\/figcaption><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>A teraz spr\u00f3bujmy zapisa\u0107 powy\u017cszy obrazek za pomoc\u0105 klas Javy. Teoretycznie powinny nam do tego wystarczy\u0107 dwie klasy \u2013 jedna reprezentuj\u0105ca \u017cyw\u0105 kaczk\u0119, a druga kaczk\u0119 elektryczn\u0105. \u017bywa kaczka potrafi p\u0142ywa\u0107 i kwaka\u0107:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_1.png\" alt=\" class=\" class=\"wp-image-30887\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 1: Klasa Duck<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Natomiast kaczka elektryczna zachowuje si\u0119 tak jak \u017cywa (i dlatego po niej dziedziczy)&#8230; O ile oczywi\u015bcie kto\u015b pami\u0119ta\u0142 i w\u0142o\u017cy\u0142 do niej baterie! W przeciwnym razie reaguje niespodziewanymi wyj\u0105tkami.<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_2.png\" alt=\" class=\" class=\"wp-image-30888\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 2: Klasa ElectricDuck<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Teraz (poniewa\u017c jeste\u015bmy w XXI wieku) zamiast tworzy\u0107 klas\u0119 z metod\u0105&nbsp;main, napiszmy test, kt\u00f3ry sprawdzi poprawno\u015b\u0107 dzia\u0142ania klasy&nbsp;Duck:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_3.png\" alt=\" class=\" class=\"wp-image-30889\" title=\"\"><figcaption class=\"wp-element-caption\">Listing 3: Klasa DuckTest<\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Stworzony test celowo sparametryzowali\u015bmy tak, aby da\u0142o si\u0119 przekaza\u0107 do niego r\u00f3\u017cne obiekty klasy&nbsp;Duck. Skoro bowiem przyj\u0119li\u015bmy, i\u017c klasa Electric Duck dziedziczy po klasie Duck, to przekazanie do testu obiektu tej klasy nie powinno niczego zepsu\u0107. Tymczasem czeka nas bardzo nieprzyjemna niespodzianka:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_4.png\" alt=\" class=\" class=\"wp-image-30890\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 4: Wyniki test\u00f3w klasy Duck<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Wygl\u0105da na to, \u017ce obiekt klasy&nbsp;ElectricDuck&nbsp;zachowuje si\u0119 niestety inaczej ni\u017c obiekt klasy&nbsp;Duck. To bardzo niedobrze, gdy\u017c w innej cz\u0119\u015bci systemu mo\u017cemy mie\u0107 metody, kt\u00f3re przyjmuj\u0105 obiekt kaczki i je\u017celi przeka\u017cemy w takim miejscu kaczk\u0119 elektryczn\u0105 (a mo\u017cemy to zrobi\u0107, gdy\u017c kaczka elektryczna dziedziczy po \u017cywej), to system zachowa si\u0119 w spos\u00f3b nieprzewidziany! W takiej sytuacji pierwsze rozwi\u0105zanie, jakie przychodzi nam do g\u0142owy, to sprawdzenie, jaki typ kaczki otrzymali\u015bmy w rzeczywisto\u015bci:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_5.png\" alt=\" class=\" class=\"wp-image-30891\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 5: Naiwny spos\u00f3b kontroli typu kaczki<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Jednak\u017ce takie podej\u015bcie:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>po pierwsze \u2013 tak naprawd\u0119 nie pozwala nam skorzysta\u0107 z zalet polimor\ufb01zmu<\/li>\n\n\n\n<li>po drugie \u2013 powoduje, \u017ce z ka\u017cdym kolejnym typem kaczki nasz kod brzydko si\u0119 rozrasta o nowy if<\/li>\n<\/ul>\n\n\n\n<p>Wygl\u0105da na to, \u017ce sytuacja, do kt\u00f3rej doprowadzili\u015bmy, ewidentnie z\u0142ama\u0142a jak\u0105\u015b regu\u0142\u0119 programowania obiektowego. Nietrudno domy\u015bli\u0107 si\u0119, \u017ce chodzi tu o jedn\u0105 z zasad SOLID, a konkretnie o odpowiadaj\u0105c\u0105 literze L \u2013 zasad\u0119 podstawienia Liskov. Zapoznajmy si\u0119 z ni\u0105 bli\u017cej.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Reguly-pani-Barbary-Liskov\">Regu\u0142y pani Barbary Liskov<\/h2>\n\n\n\n<p>Nazwa tej zasady pochodzi od nazwiska bardzo zas\u0142u\u017conej dla informatyki pani profesor Barbary Liskov [1]. Sama definicja zasady podstawienia Liskov jest do\u015b\u0107 prosta [2]:<\/p>\n\n\n\n<p><strong>Funkcje, kt\u00f3re u\u017cywaj\u0105 wska\u017anik\u00f3w lub referencji do klas bazowych, musz\u0105 by\u0107 w stanie u\u017cywa\u0107 r\u00f3wnie\u017c obiekt\u00f3w klas dziedzicz\u0105cych po klasach bazowych, bez dok\u0142adnej znajomo\u015bci tych obiekt\u00f3w.<\/strong><\/p>\n\n\n\n<p>Powy\u017csze zdanie nie wygl\u0105da ani na zbyt skomplikowane, ani na trudne do wykorzystania w praktyce. Okazuje si\u0119 jednak, \u017ce ju\u017c w bardzo prostym przypadku mamy problem z powy\u017csz\u0105 regu\u0142\u0105. Dlaczego? Odpowied\u017a tkwi w warunkach, kt\u00f3re musz\u0105 by\u0107 spe\u0142nione przez podklas\u0119 w stosunku do nadklasy, aby ta regu\u0142a by\u0142a faktycznie spe\u0142niona. A oto one [3]:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Typ zwracany przez metod\u0119 w podklasie musi by\u0107 taki sam jak w klasie bazowej albo by\u0107 jego podtypem<\/li>\n\n\n\n<li>Typy argument\u00f3w przekazywane do metody w podklasie musz\u0105 by\u0107 takie same jak w klasie bazowej \u2013 albo by\u0107 ich nadtypem<\/li>\n\n\n\n<li>Metoda podklasy mo\u017ce rzuca\u0107 wyj\u0105tek tylko wtedy, je\u015bli czyni to metoda w klasie bazowej. Typ wyj\u0105tku musi by\u0107 taki sam jak w klasie bazowej albo musi by\u0107 jego podtypem<\/li>\n\n\n\n<li>Warunki wej\u015bciowe metody w podklasie nie mog\u0105 by\u0107 silniejsze ni\u017c warunki wej\u015bciowe w klasie bazowej<\/li>\n\n\n\n<li>Warunki wyj\u015bciowe metody w podklasie nie mog\u0105 by\u0107 s\u0142absze ni\u017c warunki wyj\u015bciowe w klasie bazowej<\/li>\n\n\n\n<li>Metoda w podklasie nie mo\u017ce niszczy\u0107 niezmiennik\u00f3w klasy bazowej<\/li>\n<\/ul>\n\n\n\n<p>O ile spe\u0142nienie trzech pierwszych warunk\u00f3w skontroluje za nas kompilator Javy (w przypadku drugiego warunku nawet nadmiarowo \u2013 do metody w podklasie mo\u017cna przekaza\u0107 tylko ten sam typ argumentu, a nie nadtyp), o tyle niestety o spe\u0142nienie pozosta\u0142ych warunk\u00f3w musimy ju\u017c zadba\u0107 sami. Tu pojawia si\u0119 relatywnie wysokie ryzyko pope\u0142nienia b\u0142\u0119du. <strong>Nietrudno domy\u015ble\u0107 si\u0119, \u017ce w naszym konkretnym przypadku z\u0142amali\u015bmy czwarty warunek, poniewa\u017c w klasie&nbsp;Duck&nbsp;metoda&nbsp;getVoice()&nbsp;nie posiada \u017cadnych warunk\u00f3w wej\u015bciowych \u2013 mo\u017cna j\u0105 wywo\u0142a\u0107 w dowolnym momencie.<\/strong><\/p>\n\n\n\n<p>Natomiast w klasie Electric Duck nie jest to ju\u017c niestety prawda \u2013 metoda nie wyrzuci wyj\u0105tku tylko wtedy, gdy wcze\u015bniej wywo\u0142amy metod\u0119&nbsp;insertBatteries(). O czym kod korzystaj\u0105cy z takiego obiektu mo\u017ce nawet \u201enie wiedzie\u0107\u201d, gdy\u017c taka metoda nie wyst\u0119puje w og\u00f3le w klasie Duck!<\/p>\n\n\n\n<p>Jak wida\u0107, ze wzgl\u0119du na \u0142atw\u0105 mo\u017cliwo\u015b\u0107 z\u0142amania regu\u0142, poprawne dziedziczenie w Javie wcale nie jest proste do przeprowadzenia. Co gorsza, kompilator nie zawsze wykryje, \u017ce mia\u0142o to miejsce, a to skazuje nas na weryfikacj\u0119 kodu w spos\u00f3b r\u0119czny (a wi\u0119c \u2013 zawodny). Jakie mamy wobec tego mo\u017cliwo\u015bci rozwi\u0105zania problemu?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Czysta-abstrakcja\">Czysta abstrakcja<\/h2>\n\n\n\n<p>Pierwsz\u0105 metod\u0105, jak\u0105 mogliby\u015bmy zastosowa\u0107, jest mody\ufb01kacja istniej\u0105cej hierarchii klas tak, aby klasy reprezentuj\u0105ce konkretne byty (w naszym przypadku \u017cywa kaczka i kaczka elektryczna) dziedziczy\u0142y po klasie abstrakcyjnej, do kt\u00f3rej przenosimy maksymalnie wiele wsp\u00f3lnego kodu. Kto\u015b mo\u017ce zapyta\u0107: \u201eCo to w\u0142a\u015bciwie daje, skoro dalej zostajemy przy dziedziczeniu, a kaczka elektryczna dalej b\u0119dzie zachowywa\u0107 si\u0119 inaczej ni\u017c \u017cywa?\u201d. Ot\u00f3\u017c ca\u0142y haczyk tego rozwi\u0105zania polega na tym, i\u017c nie b\u0119dzie mo\u017cna powiedzie\u0107, \u017ce klasa dziedzicz\u0105ca \u201epsuje\u201d zachowanie klasy bazowej, gdy\u017c obiekty klasy bazowej&#8230; po prostu nie b\u0119d\u0105 istnia\u0142y (wszak jest abstrakcyjna)! A skoro nie b\u0119d\u0105 istnia\u0142y \u2013 to i nie b\u0119dzie czego zepsu\u0107.<\/p>\n\n\n\n<p>Proste \u2013 czy\u017c nie? Oczywi\u015bcie pury\u015bci programowania obiektowego zapewne stwierdz\u0105, \u017ce takie t\u0142umaczenie jest mocno naci\u0105gane&#8230; Ale to nie matematyka \u2013 tu nie ma jednego s\u0142usznego rozwi\u0105zania. Mo\u017cna mie\u0107 raczej zastrze\u017cenia co do innych wad tego podej\u015bcia \u2013 ale o nich opowiemy p\u00f3\u017aniej. Poka\u017cmy zatem nasz kod w akcji! Zacznijmy od klasy abstrakcyjnej, do kt\u00f3rej przeniesiemy wsp\u00f3lny kod. Nazwiemy j\u0105 w spos\u00f3b ma\u0142o wysublimowany \u2013&nbsp;Base Duck. Po tej klasie b\u0119d\u0105 dziedziczy\u0107 klasy reprezentuj\u0105ce \u017cywe kaczki:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_6.png\" alt=\" class=\" class=\"wp-image-30892\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 6: Klasa abstrakcyjna BaseDuck<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Klasa reprezentuj\u0105ca \u017cyw\u0105 kaczk\u0119 w tej sytuacji staje si\u0119 praktycznie pusta (gdy\u017c funkcjonalno\u015b\u0107 przesz\u0142a do klasy abstrakcyjnej) \u2013 wygl\u0105da jak wydmuszka:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_7.png\" alt=\" class=\" class=\"wp-image-30893\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 7: Praktycznie pusta klasa Duck<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Klasa odpowiedzialna za kaczk\u0119 elektryczn\u0105 zmienia si\u0119 nieco mniej, ale i w niej b\u0119d\u0105 odwo\u0142ania do klasy abstrakcyjnej:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_8.png\" alt=\" class=\" class=\"wp-image-30894\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 8: Zmody\ufb01kowana klasa ElectricDuck<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Nieco zmieniaj\u0105 si\u0119 r\u00f3wnie\u017c testy do klasy&nbsp;Duck. Poniewa\u017c klasa&nbsp;ElectricDuck&nbsp;nie dziedziczy ju\u017c po tej klasie, nie mo\u017cemy ju\u017c przetestowa\u0107 jej zachowania w tym miejscu:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_9.png\" alt=\" class=\" class=\"wp-image-30895\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 9: Zmody\ufb01kowana klasa DuckTest<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>W zwi\u0105zku z powy\u017cszym klas\u0119&nbsp;ElectricDuck&nbsp;musimy przetestowa\u0107 oddzielnie:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_10.png\" alt=\" class=\" class=\"wp-image-30896\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 10: Nowa klasa ElectricDuckTest<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Mo\u017ce pojawi\u0107 si\u0119 w tym miejscu w\u0105tpliwo\u015b\u0107: \u201eCzy nie jest b\u0142\u0119dem, \u017ce tak naprawd\u0119 zwi\u0119kszy\u0142a si\u0119 nam ilo\u015b\u0107 kodu w testach?\u201d. Wbrew pozorom, nie powinno nas to niepokoi\u0107. Du\u017ca ilo\u015b\u0107 kodu testowego (je\u017celi tylko napisanego czysto), nie jest uwa\u017cana za b\u0142\u0105d, <a href=\"https:\/\/nearshore-it.eu\/pl\/artykuly\/test-driven-development-na-co-dzien\">a w niekt\u00f3rych metodykach (np. TDD)<\/a> \u2013 jest to normalne zjawisko. Jak wida\u0107, rozwi\u0105zanie z klas\u0105 abstrakcyjn\u0105 nie wydaje si\u0119 by\u0107 zbytnio skomplikowane oraz nie wymaga du\u017cych zmian w kodzie. W wielu przypadkach to wystarcza.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Wada-tego-podejscia\">Wada tego podej\u015bcia<\/h2>\n\n\n\n<p>Sygnalizowa\u0142em wcze\u015bniej, \u017ce to rozwi\u0105zanie ma wady. Ot\u00f3\u017c ka\u017cda klasa reprezentuj\u0105ca w naszej hierarchii konkretny byt ma narzucone z g\u00f3ry, i\u017c musi dziedziczy\u0107 po klasie abstrakcyjnej. To zamyka mo\u017cliwo\u015b\u0107 dziedziczenia po czymkolwiek innym (Java nie posiada dziedziczenia wielobazowego). Mo\u017cemy bole\u015bnie odczu\u0107 skutki na w\u0142asnej sk\u00f3rze w momencie, gdy takie dziedziczenie b\u0119dzie nam potrzebne przy integracji z jakim\u015b frameworkiem lub bibliotek\u0105. Jest na to jednak spos\u00f3b \u2013 o tym, jak poradzi\u0107 sobie w sytuacji, gdy nie mo\u017cemy sobie pozwoli\u0107 na zablokowanie dziedziczenia dla naszych klas, napisz\u0119 w kolejnej cz\u0119\u015bci artyku\u0142u.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Kompozytor-wyjezdza-w-delegacje\">Kompozytor wyje\u017cd\u017ca w delegacj\u0119<\/h2>\n\n\n\n<p>Tak jak wspomina\u0142em na pocz\u0105tku, dziedziczenie cz\u0119sto stosujemy po to, aby unikn\u0105\u0107 wielokrotnego pisania kodu. Kod staje si\u0119 sk\u0142adow\u0105 klasy bazowej, a klasy dziedzicz\u0105ce mog\u0105 dzi\u0119ki temu r\u00f3wnie\u017c z niego skorzysta\u0107.<strong> Okazuje si\u0119 jednak, \u017ce podobny efekt mo\u017cna osi\u0105gn\u0105\u0107 poprzez ekstrakcj\u0119 wsp\u00f3lnego kodu do oddzielnego obiektu, a nast\u0119pnie uczynienie tego obiektu sk\u0142adnikiem klasy<\/strong>. W efekcie mo\u017cna powiedzie\u0107, \u017ce klasa nie dziedziczy jakiej\u015b funkcjonalno\u015bci, ale ta funkcjonalno\u015b\u0107 jest w ni\u0105 wkomponowana. Bardziej szczeg\u00f3\u0142owo mo\u017cna takie podej\u015bcie podzieli\u0107 na dwa nurty: agregacj\u0119 i kompozycj\u0119 [4].<\/p>\n\n\n\n<p>My w dalszej cz\u0119\u015bci artyku\u0142u b\u0119dziemy u\u017cywa\u0107 sformu\u0142owania \u201ekompozycja\u201d, aczkolwiek mo\u017ce by\u0107 ono nie do ko\u0144ca \u015bcis\u0142e. Dodatkowo wkomponowany obiekt b\u0119dziemy od tej pory nazywa\u0107 \u201edelegatem\u201d \u2013 bo tak naprawd\u0119 delegujemy do niego odpowiedzialno\u015b\u0107 za wykonanie cz\u0119\u015bci zada\u0144 naszej klasy. Tego typu podej\u015bcie nazywane jest cz\u0119sto kompozycj\u0105 ponad dziedziczeniem [5]. <strong>W jaki spos\u00f3b zrealizowaliby\u015bmy to w przypadku naszych kaczek? Przede wszystkim nale\u017ca\u0142oby stworzy\u0107 nasz obiekt \u2013 delegata:<\/strong><\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_11.png\" alt=\" class=\" class=\"wp-image-30897\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 11: Delegat zachowania kaczki<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Zauwa\u017cmy, \u017ce zawiera on mniej wi\u0119cej to samo, co mamy w klasie abstrakcyjnej&nbsp;BaseDuck. Nic w tym dziwnego \u2013 w ko\u0144cu delegat ma t\u0119 klas\u0119 zast\u0105pi\u0107. A skoro tak \u2013 to w takim razie usuwamy j\u0105, gdy\u017c nie jest ju\u017c nam potrzebna. Po usuni\u0119ciu klasy abstrakcyjnej musimy zmody\ufb01kowa\u0107 klas\u0119&nbsp;Duck. Niezb\u0119dne jest dodanie metod, kt\u00f3re dotychczas by\u0142y zdefiniowane w klasie abstrakcyjnej, oraz ich implementacja z u\u017cyciem delegata:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_12.png\" alt=\" class=\" class=\"wp-image-30898\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 12: Kaczka z delegatem<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>I podobnie robimy w klasie&nbsp;ElectricDuck:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_13.png\" alt=\" class=\" class=\"wp-image-30899\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 13: Kaczka elektryczna z delegatem<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>I to na razie wszystko! Je\u015bli uruchomimy nasze testy, to dalej powinny dzia\u0142a\u0107. Potrzebna b\u0119dzie tylko jedna zmiana \u2013 klasy, z kt\u00f3rej wykonujemy statyczne importy.<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_14.png\" alt=\" class=\" class=\"wp-image-30900\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 14: Klasa DuckTest po drobnej zmianie<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Nietrudno zauwa\u017cy\u0107, i\u017c kompozycja ma wiele zalet w por\u00f3wnaniu do klasycznego dziedziczenia:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Jest o wiele bardziej elastyczna i naturalna<\/strong> \u2013 o wiele \u0142atwiej jest opisa\u0107 struktur\u0119 obiekt\u00f3w w systemie, u\u017cywaj\u0105c kompozycji ni\u017c dziedziczenia \u2013 cz\u0119sto to drugie jest tworzone troch\u0119 \u201ena si\u0142\u0119\u201d<\/li>\n\n\n\n<li><strong>Zachowanie obiektu mo\u017cna mody\ufb01kowa\u0107 w trakcie dzia\u0142ania programu (podmieniaj\u0105c wkomponowany obiekt na inny. Przy dziedziczeniu jest to niemo\u017cliwe<\/strong> \u2013 cz\u0119\u015b\u0107 odziedziczona jest \u201esztywna\u201d<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Wady kompozycji<\/h2>\n\n\n\n<p>Oczywi\u015bcie nale\u017cy tak\u017ce wspomnie\u0107 o wadach kompozycji:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Zwi\u0119ksza si\u0119 zu\u017cycie pami\u0119ci \u2013 u\u017cywamy bowiem wi\u0119kszej liczby ma\u0142ych obiekt\u00f3w<\/li>\n\n\n\n<li>Lekko spada wydajno\u015b\u0107 wywo\u0142ywania metod \u2013 wywo\u0142uj\u0105c metod\u0119naobiekcie, tak naprawd\u0119 musimy wywo\u0142a\u0107 metod\u0119 z delegata \u2013 jest to jedna ramka na stosie wi\u0119cej<\/li>\n\n\n\n<li>Klasy maj\u0105ce t\u0119 sam\u0105 funkcjonalno\u015b\u0107 i metody (w naszym przypadku&nbsp;Duck&nbsp;i&nbsp;ElectricDuck) staj\u0105 si\u0119 zupe\u0142nie roz\u0142\u0105czne w hierarchii typ\u00f3w, co w przypadku niekt\u00f3rych j\u0119zyk\u00f3w uniemo\u017cliwia proste zast\u0119powanie ich wzajemnie.<\/li>\n\n\n\n<li>Powstaje sporo metod, kt\u00f3rych jedynym zadaniem jest wywo\u0142anie odpowiedniej metody delegata (tzw. metody przekazuj\u0105ce).<\/li>\n<\/ul>\n\n\n\n<p>Dwie ostatnie wady s\u0105 zale\u017cne od j\u0119zyka, z jakiego korzystamy, i w niekt\u00f3rych j\u0119zykach programowania nie wyst\u0119puj\u0105. Niestety w przypadku Javy tak nie jest i trzeba w\u0142o\u017cy\u0107 wi\u0119cej pracy, \u017ceby si\u0119 od nich uwolni\u0107. Pisz\u0119 o tym w dw\u00f3ch kolejnych cz\u0119\u015bciach artyku\u0142u.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Kaczki-a-kacze-typowanie\">Kaczki, a kacze typowanie<\/h2>\n\n\n\n<p>W poprzedniej cz\u0119\u015bci wspomnieli\u015bmy, i\u017c nasze klasy reprezentuj\u0105ce kaczki sta\u0142y si\u0119 zupe\u0142nie roz\u0142\u0105czne w hierarchii typ\u00f3w. W zwi\u0105zku z tym, mimo tego, i\u017c wsp\u00f3\u0142dziel\u0105 de facto te same metody <strong>(getVoice()&nbsp;i&nbsp;getSwimVoice()),<\/strong> dla kompilatora Javy s\u0105 to zupe\u0142nie r\u00f3\u017cne byty.<\/p>\n\n\n\n<p>Dlaczego tak si\u0119 dzieje?<strong> Ot\u00f3\u017c j\u0119zyk Java jest j\u0119zykiem z siln\u0105 kontrol\u0105 typ\u00f3w i o tym, jakie metody mo\u017cna wywo\u0142a\u0107 na danym obiekcie, decyduje tylko i wy\u0142\u0105cznie jego typ (klasa i implementowane interfejsy).<\/strong> Dlatego te\u017c nie ma w nim zastosowania tak zwane (nomen omen) kacze typowanie \u2013 w kt\u00f3rym o mo\u017cliwo\u015bciach obiektu nie decyduje jego typ, lecz udost\u0119pnione przeze\u0144 metody [6]:<\/p>\n\n\n\n<p>Je\u015bli co\u015b chodzi jak kaczka i kwacze jak kaczka, to musi by\u0107 kaczk\u0105.<\/p>\n\n\n\n<p>Teoretycznie w Javie mo\u017cna wprowadzi\u0107 kacze typowanie, posi\u0142kuj\u0105c si\u0119 mechanizmem re\ufb02eksji. Powsta\u0142y w ten spos\u00f3b kod wygl\u0105da jednak bardzo nieczytelnie. Co gorsza, pozbawia nas silnej kontroli typ\u00f3w (co moim zdaniem jest jednak spor\u0105 i cz\u0119sto niedocenian\u0105 zalet\u0105 j\u0119zyka Java). Na szcz\u0119\u015bcie jest te\u017c do zastosowania o wiele prostszy spos\u00f3b \u2013 wystarczy wprowadzi\u0107 interfejs opisuj\u0105cy zachowanie kaczki. W przeciwie\u0144stwie do dziedziczenia (gdzie mo\u017cemy mie\u0107 maksymalnie jednego przodka), ka\u017cda klasa mo\u017ce implementowa\u0107 dowoln\u0105 ilo\u015b\u0107 interfejs\u00f3w. Wprowad\u017amy zatem taki interfejs&nbsp;DuckBehaviour:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_15.png\" alt=\" class=\" class=\"wp-image-30901\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 15: Interfejs opisuj\u0105cy zachowanie kaczki<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>A nast\u0119pnie zaimplementujmy go w klasach opisuj\u0105cych kaczki (nietrudno si\u0119 domy\u015bli\u0107, \u017ce nie wymaga to wielkich zmian)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_16.png\" alt=\" class=\" class=\"wp-image-30902\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 16: Kaczka z interfejsem<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_17.png\" alt=\" class=\" class=\"wp-image-30903\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 17: Kaczka elektryczna z interfejsem<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><strong>Jak wida\u0107, rozwi\u0105zanie z delegatem pozwoli\u0142o nam pozby\u0107 si\u0119 problem\u00f3w z b\u0142\u0119dnym dziedziczeniem.<\/strong> Jednocze\u015bnie kod odpowiedzialny za bazowe zachowanie kaczki istnieje tylko w jednym miejscu (w klasie delegata). Czy\u017cby sukces? Niestety jeszcze nie! Rado\u015b\u0107 psuj\u0105 nam bowiem metody przekazuj\u0105ce w klasach&nbsp;Duck&nbsp;i&nbsp;ElectricDuck. Musimy je napisa\u0107, aby odbywa\u0142o si\u0119 wywo\u0142anie odpowiedniej metody w delegacie. Co gorsza \u2013 te metody b\u0119dziemy musieli stworzy\u0107 w ka\u017cdej klasie reprezentuj\u0105cej kaczk\u0119, wi\u0119c nie wygl\u0105da to za dobrze. Czy\u017cby \u201ezamieni\u0142 stryjek siekierk\u0119 na kijek?\u201d Aby zaradzi\u0107 temu problemowi, b\u0119dziemy musieli jeszcze nieco przerobi\u0107 kod i wezwa\u0107 na ratunek dobrze wszystkim znanego Lomboka<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Lombok-na-ratunek\"><strong>Lombok na ratunek<\/strong><\/h2>\n\n\n\n<p><strong>Problem z metodami przekazuj\u0105cymi tak naprawd\u0119 wynika z faktu, i\u017c j\u0119zyk Java sam w sobie nie wspiera mechanizmu delegacji (w przeciwie\u0144stwie do j\u0119zyk\u00f3w takich jak Groovy czy Kotlin, pozostaj\u0105c w \u015bwiecie JVM).<\/strong> Wiemy te\u017c, \u017ce cz\u0119sto takie braki j\u0119zyka s\u0105 w ca\u0142kiem rozs\u0105dny spos\u00f3b \u201e\u0142atane\u201d przez bibliotek\u0119 Lombok (\u017ceby np. wspomnie\u0107 o setterach i getterach).<\/p>\n\n\n\n<p>Tak jest i tym razem, ale jest pewien haczyk. Lombok oferuje adnotacj\u0119&nbsp;@Delegate, kt\u00f3ra rozwi\u0105zuje nasz problem. Nale\u017cy jednak mie\u0107 na uwadze, \u017ce jest ona traktowana jako eksperymentalna. Oznacza to, \u017ce mo\u017ce w przysz\u0142o\u015bci znikn\u0105\u0107. Nie jest to idealna sytuacja, ale dobre i to! Aby m\u00f3c skorzysta\u0107 z tej adnotacji, musimy doprowadzi\u0107 do sytuacji, w kt\u00f3rej wszystkie metody przekazuj\u0105ce nie zawieraj\u0105 \u017cadnej logiki poza wywo\u0142aniem odpowiadaj\u0105cej metody delegata. W klasie&nbsp;Duck&nbsp;nie jest to problemem, gdy\u017c ju\u017c tak si\u0119 dzieje. Natomiast w klasie&nbsp;ElectricDuck&nbsp;metody przekazuj\u0105ce wykonuj\u0105 dodatkowe sprawdzenie (czy baterie s\u0105 w\u0142o\u017cone).<\/p>\n\n\n\n<p>Musimy zatem wyci\u0105gn\u0105\u0107 te sprawdzenia na zewn\u0105trz, korzystaj\u0105c ze wzorca strategii i takie strategie przekaza\u0107 do delegata. W naszym przypadku b\u0119d\u0105 to zwyk\u0142e dwa pola typu&nbsp;Runnable&nbsp;reprezentuj\u0105ce blok kodu do wykonania przed wydaniem g\u0142osu i pr\u00f3b\u0105 p\u0142yni\u0119cia:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_18.png\" alt=\" class=\" class=\"wp-image-30904\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 18: Delegat ze strategiami<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Dodatkowo do\u0142o\u017cyli\u015bmy sta\u0142\u0105 reprezentuj\u0105c\u0105 \u201epust\u0105\u201d strategi\u0119 nierobi\u0105c\u0105 niczego. U\u0142atwi nam to za chwil\u0119 przerobienie klasy&nbsp;Duck. Samo u\u017cycie adnotacji&nbsp;@Delegate&nbsp;jest proste \u2013 wystarczy u\u017cy\u0107 jej na polu reprezentuj\u0105cym delegata. Nast\u0119pnie usuwamy metody przekazuj\u0105ce \u2013 i to wszystko!<\/p>\n\n\n\n<p>Klasa&nbsp;Duck&nbsp;b\u0119dzie prezentowa\u0107 si\u0119 nast\u0119puj\u0105co:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/2020.09.30_code_19.png\" alt=\" class=\" class=\"wp-image-30905\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 19: Klasa Duck z adnotacj\u0105 @Delegate<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Nietrudno w takim razie przerobi\u0107 i klas\u0119&nbsp;ElectricDuck:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/pl\/artykuly\/wp-content\/uploads\/2023\/04\/2020.09.30_code_20.png\" alt=\"\" class=\"wp-image-30906\" style=\"object-fit:cover\" title=\"\"><figcaption class=\"wp-element-caption\"><em>Listing 20: Klasa ElectricDuck z adnotacj\u0105 @Delegate<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Mo\u017cemy odetchn\u0105\u0107 z ulg\u0105, bo wynikowy kod wygl\u0105da ju\u017c o niebo lepiej! Mo\u017cemy teraz przyjrze\u0107 si\u0119 efektom pracy i spr\u00f3bowa\u0107 przej\u015b\u0107 jeszcze raz wszystkie kroki.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Czas-na-podsumowanie\">Czas na podsumowanie<\/h2>\n\n\n\n<p>Jak pokaza\u0142 nam przedstawiony przyk\u0142ad, tworz\u0105c hierarchi\u0119 obiekt\u00f3w w Javie, mo\u017cna relatywnie \u0142atwo pope\u0142ni\u0107 b\u0142\u0105d przy dziedziczeniu z istniej\u0105cej klasy. Co prawda powinna nas chroni\u0107 przed tym zasada podstawienia Liskov, ale niestety \u0142atwo j\u0105 nie\u015bwiadomie z\u0142ama\u0107. <strong>Dlatego zamiast prostego dziedziczenia warto rozwa\u017cy\u0107 inne podej\u015bcia, takie jak wprowadzenie klasy abstrakcyjnej (prostsze) lub zast\u0105pienie dziedziczenia kompozycj\u0105 (bardziej z\u0142o\u017cone, ale daj\u0105ce wi\u0119ksz\u0105 elastyczno\u015b\u0107).<\/strong><\/p>\n\n\n\n<p>W przypadku zastosowania kompozycji nale\u017cy tak\u017ce pami\u0119ta\u0107, i\u017c z powodu silnej kontroli typ\u00f3w w Javie, nale\u017cy stworzy\u0107 interfejs zawieraj\u0105cy wsp\u00f3lne metody klas, tak aby w przysz\u0142o\u015bci nie zamyka\u0107 sobie drogi do zamiany obiektu jednego typu na obiekt drugiego typu. Z kolei nadmiarowy kod spowodowany u\u017cyciem delegat\u00f3w mo\u017cna skr\u00f3ci\u0107 z wykorzystaniem (eksperymentalnej co prawda) adnotacji&nbsp;@Delegate&nbsp;z Lomboka. Wymaga to czasami ekstrakcji dodatkowych interfejs\u00f3w w delegacie, tak aby mo\u017cna by\u0142o zastosowa\u0107 wzorzec strategii, ale w efekcie ko\u0144cowym otrzymujemy maksymalnie prosty i elastyczny kod. Warto wi\u0119c rozwa\u017cy\u0107 i t\u0119 opcj\u0119.<\/p>\n\n\n\n<p>Oczywi\u015bcie kod zawarty w niniejszym artykule jest dost\u0119pny publicznie w moim&nbsp;<a href=\"https:\/\/github.com\/chrosciu\/ducks\" target=\"_blank\" rel=\"noopener\">repozytorium na GitHubie<\/a>. Zapraszam zatem do klonowania i eksperymentowania na w\u0142asn\u0105 r\u0119k\u0119. Przyjemnego tworzenia hierarchii i niech Wam obiekty lekkimi b\u0119d\u0105!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Literatura<\/strong><\/h3>\n\n\n\n<p>[1] \u201eBarbara Liskov [online]. Wikipedia: wolna encyklopedia\u201d,, 2020-07-13 12:48Z. [dost\u0119p: 2020-07-20 13:24Z]. <a href=\"https:\/\/pl.wikipedia.org\/wiki\/Barbara_Liskov?oldid=60392345\" target=\"_blank\" rel=\"noopener\">Dost\u0119pny w Internecie<\/a>.<br>[2] \u201eZasada podstawienia Liskov [online]. Wikipedia, wolna encyklopedia\u201d, 2020-04-17 21:56Z. [dost\u0119p: 2020-07-17 13:45Z]. <a href=\"https:\/\/pl.wikipedia.org\/wiki\/Zasada_podstawienia_Liskov?oldid=%2059480242\" target=\"_blank\" rel=\"noopener\">Dost\u0119pny w Internecie<\/a>.<br>[3] Wikipedia contributors, \u201eLiskov substitution principle \u2014 <a href=\"https:\/\/en.wikipedia.org\/w\/index.php?title=Liskov_substitution_principle&amp;oldid=966496218\" target=\"_blank\" rel=\"noopener\">Wikipedia, the free encyclopedia<\/a>\u201d, 2020. [Online; accessed 17July-2020].<br>[4] \u201eAgregacja (programowanie obiektowe) [online]. Wikipedia, Wolna encyklopedia\u201d, 2020-03-20 12:29Z. [dost\u0119p: 2020-07-20 10:36Z]. <a href=\"https:\/\/pl.wikipedia.org\/wiki\/Agregacja_(programowanie_obiektowe)%20?oldid=59130793\" target=\"_blank\" rel=\"noopener\">Dost\u0119pny w Internecie<\/a>.<br>[5] Wikipedia contributors, \u201eComposition over inheritance \u2014 <a href=\"https:\/\/en.wikipedia.org\/w\/index.php?title=Composition_over_inheritance&amp;oldid=967172104\" target=\"_blank\" rel=\"noopener\">Wikipedia, the free encyclopedia<\/a>\u201d [Online; accessed 20-July-2020].<br>[6] \u201eDuck typing [online]&#8221;. Wikipedia, wolna encyklopedia, 2020-04-17 21:54Z. [dost\u0119p: 2020-07-20 12:06Z]. <a href=\"https:\/\/pl.wikipedia.org\/wiki\/Duck_typing?oldid=59480107\" target=\"_blank\" rel=\"noopener\">Dost\u0119pny w Internecie<\/a>.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Poznaj problemy zwi\u0105zane z dziedziczeniem i hierarchi\u0105 klas w Javie oraz alternatywne podej\u015bcia, kt\u00f3re te problemy rozwi\u0105zuj\u0105.<\/p>\n","protected":false},"author":91,"featured_media":30843,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"iawp_total_views":34,"footnotes":""},"categories":[1,582],"tags":[568],"offering":[522],"class_list":["post-30781","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artykuly","category-technologie","tag-java-pl","offering-tech-blog"],"acf":[],"_links":{"self":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/30781","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/users\/91"}],"replies":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/comments?post=30781"}],"version-history":[{"count":4,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/30781\/revisions"}],"predecessor-version":[{"id":33844,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/30781\/revisions\/33844"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media\/30843"}],"wp:attachment":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media?parent=30781"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/categories?post=30781"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/tags?post=30781"},{"taxonomy":"offering","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/offering?post=30781"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}