Kod

Implementacja wzorca dekoratora w Java

Implementacja wzorca dekoratora w Java

Bezpłatny kurs Pythona ➞ Mini kurs dla początkujących i doświadczonych programistów. 4 fajne projekty do Twojego portfolio, czat na żywo z prelegentem. Kliknij i dowiedz się, czego możesz się nauczyć na kursie.

Dowiedz się więcej

W tym artykule kontynuujemy zgłębianie wzorców projektowych. W pierwszej części zagłębiliśmy się w pracę z danymi z łazika marsjańskiego NASA. Dzisiaj stworzymy wyjątkowy wszechświat i zaludnimy go istotami o wyjątkowych supermocach. Ten proces wymaga starannego podejścia do projektowania, aby zapewnić harmonijną interakcję wszystkich elementów. Wykorzystując różne wzorce projektowe, możemy skutecznie zorganizować strukturę naszego wszechświata i jego mieszkańców, co pozwoli nam stworzyć fascynujący i funkcjonalny świat.

  • Opis problemu
  • Rozwiązanie 1: Utwórz wiele indywidualnych klas
  • Rozwiązanie 2: Przenieś całą logikę do klasy bazowej
  • Rozwiązanie 3: Zastosuj wzorzec Dekorator
  • Podsumowując

Opis problemu

Stworzenie programu porównującego dwóch superbohaterów i określającego, który z nich ma większe szanse na przetrwanie bitwy, jest ciekawym problemem. Program ten musi analizować różne cechy superbohaterów, takie jak siła, szybkość, inteligencja, doświadczenie i specjalne zdolności. Ważne jest, aby wziąć pod uwagę nie tylko parametry fizyczne, ale także umiejętności strategiczne i słabości każdego bohatera.

Aby wdrożyć ten program, można użyć algorytmów, które ocenią każdą z cech i przypiszą im określone wagi. Pozwoli to na uzyskanie dokładniejszych wyników porównań. Warto również dodać możliwość uwzględnienia kontekstu bitwy, takiego jak środowisko i warunki, w których toczy się bitwa.

Dzięki temu program stanie się potężnym narzędziem do analizy i przewidywania wyników walk między superbohaterami, co zainteresuje zarówno fanów komiksów, jak i twórców gier.

W naszym wyjątkowym świecie istnieją dwa rodzaje bohaterów: zieloni i czerwoni. Każdy z nich może posiadać jedną lub więcej supermocy, takich jak supersiła, superzręczność i superinteligencja. Zdolności te pozwalają im dokonywać niezwykłych czynów i chronić świat przed zagrożeniami. Zieloni bohaterowie zazwyczaj reprezentują nadzieję i harmonię, podczas gdy czerwoni ucieleśniają siłę i determinację. Ich interakcja tworzy dynamikę, która przyciąga uwagę i inspiruje.

Los bohatera jest bezpośrednio powiązany z jego szansą na przetrwanie. Czerwoni bohaterowie mają początkowo nieco większe szanse, ale każda aktywowana supermoc znacząco zwiększa prawdopodobieństwo sukcesu. Im więcej unikalnych zdolności używa bohater, tym więcej szans ma na przetrwanie w sytuacjach krytycznych. Dlatego strategiczne wykorzystanie supermocy staje się kluczowym elementem zwiększającym szanse bohatera na przetrwanie.

  • super siła – o 4;
  • super zręczność – o 3;
  • super inteligencja – o 7.

W walce dwóch superbohaterów wygrywa ten, który ma większą szansę na ostateczne zwycięstwo. Zasada ta ma zastosowanie w różnych scenariuszach, w których wynik zależy od prawdopodobieństwa. W takich bitwach kluczowym czynnikiem jest nie tylko siła bohaterów, ale także ich decyzje taktyczne, umiejętność wykorzystania swoich zdolności i analizy sytuacji. Im wyższa szansa na ostateczne zwycięstwo, tym większe prawdopodobieństwo sukcesu w konfrontacji.

W tym zadaniu skupiamy się wyłącznie na cechach bohatera i jego zdolnościach przetrwania. Główny interfejs superbohatera będzie reprezentowany przez następujące elementy: opis jego wyglądu, unikalne umiejętności, poziom sprawności fizycznej i cechy adaptacyjne. Te aspekty pomogą stworzyć pełny obraz bohatera i jego potencjału w trudnych sytuacjach.

Kurs Zielonego Superbohatera to wyjątkowa okazja do nauki i rozwijania umiejętności, które czynią z niego wybitnego obrońcę środowiska. Kurs ten podkreśla znaczenie ochrony przyrody i zrównoważonego rozwoju. Zielony Superbohater wykorzystuje swoje moce nie tylko do walki z przestępczością, ale także do ochrony ekosystemów i podnoszenia świadomości na temat problemów środowiskowych. Unikalne umiejętności, takie jak manipulowanie żywiołami, tworzenie ekologicznych technologii i zdolność inspirowania innych do działań na rzecz ochrony planety, czynią go prawdziwym symbolem walki o sprawiedliwość ekologiczną. Dołącz do kursu Zielonego Superbohatera i stań się częścią ruchu na rzecz czystej i bezpiecznej planety.

Aby uzyskać bardziej stabilny i żywy czerwony kolor, wybierz odmiany o wysokiej witalności. Rośliny te nie tylko przyciągają uwagę bogatą barwą, ale także zachowują swoje piękno przez długi czas, nawet w niesprzyjających warunkach. Upewnij się, że wybrana odmiana jest odpowiednia dla Twojego regionu i klimatu. Właściwa pielęgnacja, w tym regularne podlewanie i nawożenie, zapewni roślinie zdrowie i bogactwo kolorów przez cały sezon.

Stwórz wiele indywidualnych klas

Możesz pójść za przykładem Marvela i nadać każdemu możliwemu wariantowi bohatera unikalną nazwę, tworząc osobną klasę. Na przykład, zielony superbohater o potężnej sile i wyjątkowej inteligencji mógłby nazywać się Hulk, a czerwony bohater o wysokiej inteligencji i wyjątkowej zwinności – Spider-Man. Pozwala to na tworzenie różnorodnych postaci i uczynienie ich rozpoznawalnymi, co jest ważne dla wzbudzenia zainteresowania i zaangażowania odbiorców.

W sumie powstanie 16 klas, z czego osiem to kombinacje trzech supermocy dla postaci czerwonych, a pozostałe osiem dla postaci zielonych.

Klasa Hulka reprezentuje potężne i unikalne ucieleśnienie siły i zniszczenia. Znana ze swojego niesamowitego potencjału fizycznego, postać ta stała się symbolem niepohamowanej furii i ochrony. Stworzony w oparciu o eksperyment naukowy, Hulk demonstruje transformację z człowieka w gigantyczne zielone stworzenie, gdy staje w obliczu silnych emocji, takich jak gniew. Jego zdolności obejmują nie tylko kolosalną siłę, ale także szybki czas regeneracji, co czyni go praktycznie niezniszczalnym. W świecie komiksów Hulk stał się ikoną, odzwierciedlając walkę z wewnętrznymi demonami i poszukiwanie swojego miejsca w społeczeństwie. Ta postać wciąż inspiruje i zadziwia, pozostając istotną w różnych formatach medialnych, od komiksów po film.

Liczba klas jest już duża, a wraz z dodawaniem każdej nowej supermocy ich liczba podwoi się dla każdego typu superbohatera. Może to skomplikować balansowanie gry i postrzeganie różnych klas przez graczy.

Jeśli zdecydujemy się dodać nie supermoc, ale nowy typ, na przykład żółtych superbohaterów, liczba klas wzrośnie o 2n, gdzie n to liczba wariantów supermocy. Zatem rozszerzenie klasyfikacji superbohaterów może znacznie zwiększyć różnorodność postaci i ich unikalne cechy.

Aby obliczyć niezbędne informacje, należy wziąć pod uwagę kilka kluczowych czynników. Najpierw należy określić parametry, które należy zmierzyć. Następnie należy zebrać wszystkie dane, które mogą wpłynąć na wynik. Należy użyć odpowiednich wzorów lub metod obliczeniowych, aby uzyskać dokładne wartości. Ważne jest również sprawdzenie wyników pod kątem dokładności i zgodności z oczekiwaniami. Regularna analiza i weryfikacja obliczeń pomogą lepiej zrozumieć proces i zwiększyć jego skuteczność.

Każdy typ bohatera, czy to czerwony, czy zielony, ma dwa główne stany: obecność supermocy lub jej brak.

Każdy z przedstawionych wariantów ma albo superzręczność, albo jej brak. Zatem dla każdej z dwóch opcji istnieją dwie dodatkowe podopcje, co daje łącznie cztery możliwe kombinacje.

Każda z czterech rozważanych opcji superinteligencji może mieć następujące cechy: może być obecna lub nieobecna.

Liczba 4 razy 2 równa się 8. To wyrażenie to proste działanie matematyczne, które pokazuje, że mnożąc cztery przez dwa, otrzymujemy osiem. Mnożenie jest jednym z podstawowych działań arytmetycznych, a zrozumienie jego zasad jest ważne dla rozwiązywania bardziej złożonych problemów. W tym przypadku 8 opcji może reprezentować różne kombinacje lub wyniki, które można uzyskać, używając tych liczb w różnych kontekstach.

Każda supermoc podwaja liczbę możliwych opcji superbohatera dla każdego koloru. Oznacza to, że przy n supermocach całkowita liczba unikalnych superbohaterów wynosi 2 do potęgi n, co podkreśla różnorodność i potencjał postaci w tej kategorii.

Tworząc nową klasę, należy ręcznie zdefiniować opis i opracować metodę obliczania szans na przeżycie. Łatwo się pogubić w takim gąszczu, a wszelkie zmiany mogą prowadzić do znacznych trudności. Efektywne zarządzanie klasami i ich parametrami jest kluczowym aspektem udanej pracy w tym systemie.

Przenieś całą logikę do klasy bazowej

Zmniejszmy liczbę klas, pozostawiając tylko klasy dla czerwonych i zielonych superbohaterów. Zamiast tego utworzymy klasę bazową, która zawiera metody opisujące bohatera i obliczające jego szanse na przeżycie. Zoptymalizuje to strukturę kodu i uprości dalszy rozwój, zapewniając jednocześnie elastyczność i możliwość rozszerzenia funkcjonalności.

Klasa bazowa będzie wyglądać następująco:

Niewielkie zmiany należy wprowadzić w klasach RedSuperHero i GreenSuperHero, aby dziedziczyły one po klasie bazowej. Na przykład, dla czerwonej klasy superbohatera wyglądałoby to następująco:

Aby stworzyć czerwonego superbohatera z superinteligencją i superzwinnością, należy utworzyć instancję klasy RedSuperHero i ustawić odpowiednie właściwości na true. Dzięki temu Twoja postać będzie miała unikalne umiejętności, które wyróżnią ją spośród innych bohaterów. Upewnij się, że wszystkie niezbędne parametry są ustawione poprawnie, aby zmaksymalizować potencjał Twojego superbohatera.

Jest mniej klas, a cała logika jest teraz skoncentrowana w jednym miejscu. Upraszcza to zarządzanie kodem i poprawia czytelność. Usprawniona struktura ułatwia wprowadzanie zmian i ulepszeń, co ostatecznie przyczynia się do bardziej efektywnego rozwoju i utrzymania projektu. Skupienie logiki w jednym miejscu zmniejsza również prawdopodobieństwo błędów i poprawia testowalność kodu.

  • Dodając nową umiejętność, będziesz musiał dodać właściwości do klasy bazowej i zmienić logikę implementacji metod – teraz metody staną się dłuższe i bardziej złożone;
  • Jeśli chcemy nieco skomplikować nasz świat i stwierdzić, że nie wszystkie typy bohaterów mogą mieć wszystkie supermoce, klasy pochodne nadal będą zawierać wszystkie metody klasy bazowej – dziedziczenie nie daje innej możliwości;
  • Podwojenie lub potrojenie jakiejkolwiek supermocy – na przykład nadanie bohaterowi podwójnych supermocy – w takiej strukturze klas również nie zadziała: będziesz potrzebować więcej klas lub nowych właściwości.

Aby zapewnić różnorodność i wyeliminować wady dziedziczenia, zaleca się użycie wzorca projektowego Dekorator. Wzorzec ten pozwala dynamicznie dodawać nowe funkcjonalności do obiektów bez zmiany ich struktury. Dekorator zapewnia elastyczność i rozszerzalność, umożliwiając tworzenie złożonych obiektów z prostych poprzez łączenie różnych dekoratorów. Jest to szczególnie przydatne w przypadkach, gdy konieczne jest uniknięcie złożonych hierarchii klas i uproszczenie zarządzania zmianami w systemie. Użycie wzorca Dekorator sprzyja lepszemu przestrzeganiu zasad SOLID i poprawia czytelność kodu.

Używanie wzorca dekoratora

Wzorzec projektowy Gang of Four, znany jako dekorator, pozwala dynamicznie dodawać nowe zachowania do obiektu. To podejście jest alternatywą dla tworzenia wielu klas pochodnych, które implementują dane zachowanie. Używanie dekoratorów zapewnia większą elastyczność i upraszcza kod, umożliwiając programistom rozszerzanie funkcjonalności obiektów bez zmiany ich oryginalnej klasy. Takie podejście promuje zasadę pojedynczej odpowiedzialności oraz poprawia czytelność i łatwość utrzymania kodu.

Wzorzec dekoratora ma opisową nazwę: pobiera dany obiekt i dodaje do niego dodatkowe funkcjonalności lub właściwości. Niezależnie od użytych dekoracji, wynik końcowy powinien pozostać obiektem tego samego typu, ale o rozszerzonych możliwościach. Na przykład po zastosowaniu dekoratora do superbohatera, powinien on pozostać superbohaterem z nowymi supermocami, a nie przekształcać się w zupełnie inne stworzenie, takie jak Transformer lub dinozaur.

Dlatego ważne jest, aby zrozumieć, że skuteczne zaangażowanie odbiorców wymaga ostrożnego podejścia. Aby osiągnąć sukces, konieczne jest uwzględnienie zainteresowań i potrzeb grupy docelowej. Pozwala to na tworzenie treści, które nie tylko przyciągną uwagę, ale także utrzymają zainteresowanie użytkowników. Ważne jest również zoptymalizowanie tekstu pod kątem wyszukiwarek, w tym słów kluczowych i fraz, które pomogą poprawić widoczność witryny. Wysokiej jakości treść pomaga budować zaufanie do marki i zwiększa prawdopodobieństwo konwersji. Nie zapomnij o regularnej aktualizacji informacji, co pomoże utrzymać trafność i atrakcyjność witryny.

  • Klasa dekoratora musi być tego samego typu, co klasa dekorowana — implementować ten sam interfejs lub dziedziczyć tę samą klasę bazową.
  • Dekorator implementuje zachowanie oryginalnej klasy; Nie zmienia jej, ale dodaje własne zachowanie przed lub po wywołaniu klasy bazowej.
  • Osiąga się to dzięki temu, że dekorator zawiera obiekt klasy bazowej i wywołuje jego metody w miejscach, w których zachowanie musi zostać uzupełnione.

Oto reprezentacja na diagramie klas:

Diagram klas. Infografiki: Maya Malgina dla Skillbox Media

Istnieją dwa główne typy klas: klasy „zwykłe”, znane jako ConcreteComponents, oraz klasy dekoratorów, które dodają do nich nowe możliwości. Klasy dekoratorów nazywane są również klasami opakowującymi, ponieważ zawierają obiekt, który zapewnia dodatkową funkcjonalność. Pozwala to na elastyczne rozszerzanie działania klas zwykłych bez zmiany ich wewnętrznej struktury.

Wyobraźmy sobie wielowarstwową odzież, w której każda warstwa pełni swoją ważną funkcję. Pierwsza warstwa skutecznie odprowadza wilgoć od ciała, zapewniając komfort i suchość. Druga warstwa zatrzymuje ciepło, tworząc przytulną atmosferę nawet w chłodne dni. Trzecia warstwa chroni przed wiatrem i deszczem, zapewniając niezawodną barierę. Razem te warstwy tworzą ochronny „kokon”, pozwalający człowiekowi skutecznie radzić sobie ze zmiennymi warunkami pogodowymi. Takie podejście do wyboru ubrań pozwala zachować aktywność i wygodę w każdym klimacie.

Wróćmy do tematu superbohaterów i rozważmy zastosowanie wzorca Dekorator. Zacznijmy od utworzenia bazowej klasy dekoratora. Klasa ta stanie się podstawą do dodawania nowych funkcjonalności do obiektów superbohaterów bez zmiany ich kodu źródłowego. Wzorzec Dekorator pozwala na dynamiczne rozszerzanie zachowania obiektów, co jest szczególnie przydatne w sytuacjach, gdy trzeba dodać superbohaterowi nowe cechy lub umiejętności. Teraz możemy przejść do implementacji bazowego dekoratora, który pozwoli nam elastycznie modyfikować obiekty, zachowując ich oryginalne właściwości.

Ta klasa jest abstrakcyjna, co uniemożliwia tworzenie jej instancji. Nie jest to konieczne, ponieważ jej główną funkcją jest przechowywanie danych o superbohaterze. Używamy metod klasy, aby uzyskać informacje o cechach bohatera i prawdopodobieństwie jego przeżycia. To podejście pozwala na efektywną organizację struktury kodu, minimalizując redundancję i zapewniając łatwość pracy z danymi.

Jeśli nie znasz jeszcze klas abstrakcyjnych, zalecamy zapoznanie się z tym artykułem. Znajdziesz w nim pełne wyjaśnienie koncepcji klas abstrakcyjnych, ich funkcji i zastosowań w programowaniu. Zrozumienie tego tematu pomoże Ci poprawić umiejętności programistyczne i tworzyć wydajniejsze i bardziej ustrukturyzowane programy.

Stwórzmy unikalny dekorator, który nada Twojej funkcji wyjątkową moc. Ten dekorator pozwoli Ci łatwo rozszerzać funkcjonalność bez zmiany kodu źródłowego. Doda dodatkowe funkcje, takie jak rejestrowanie, kontrola dostępu czy buforowanie wyników. Prawidłowo zaimplementowany dekorator nie tylko usprawni kod, ale także sprawi, że będzie on bardziej czytelny i łatwiejszy w utrzymaniu. Przyjrzyjmy się teraz, jak utworzyć taki dekorator i zastosować go do naszej funkcji, aby wykorzystać jej potencjał.

Klasy opakowujące dla superzręczności i superinteligencji są tworzone na podobnej zasadzie. Klasy te pozwalają na efektywne zarządzanie funkcjonalnością i zapewniają elastyczność w korzystaniu z różnych cech. Prawidłowa implementacja klas opakowujących pomaga zoptymalizować kod i poprawić jego czytelność, co jest szczególnie ważne dla programistów dążących do poprawy wydajności swoich aplikacji. Przyjrzyjmy się mocy dekoratorów, inscenizując prawdziwą bitwę superbohaterów. W tym kontekście dekoratory posłużą jako narzędzie, które dodaje nową funkcjonalność i rozszerza możliwości naszych bohaterów. Zobaczymy, jak mogą one modyfikować zachowanie, rozwijać umiejętności i dostosowywać się do różnych warunków. Ta demonstracja pozwoli nam wyraźnie zrozumieć, jak dekoratory mogą przekształcać funkcjonalność, czyniąc ją bardziej elastyczną i wydajną. Bitwa superbohaterów będzie doskonałym przykładem dekoratorów w programowaniu, demonstrując ich moc i wszechstronność w tworzeniu złożonych i bogatych w funkcje systemów.

Szanse na wygraną są niemal równe, ale ostatecznie zwycięży zielony superbohater, który posiada zarówno superinteligencję, jak i niezwykłą zwinność.

Wynik konsoli po uruchomieniu programu. Zrzut ekranu: Ekaterina Stepanova dla Skillbox Media

Każdy dekorator, wywołując metodę, na przykład w celu obliczenia szansy przeżycia, wywołuje metodę obiektu o tej samej nazwie, który został zapisany w konstruktorze. Po otrzymaniu wartości z tej metody, dekorator dodaje do wyniku swój własny numer, oparty na określonej supermocy. W ten sposób dekoratory pozwalają rozszerzyć funkcjonalność obiektów, dodając nowe możliwości i ulepszając ich cechy.

Należy pamiętać, że obiekt zapisany w konstruktorze również może zostać udekorowany. W tym przypadku nasza metoda nie będzie mogła od razu zwrócić wartości, ale będzie musiała zagłębić się w strukturę obiektu. Musi uzyskać dostęp do obiektu wewnętrznego, który znajduje się już w drugim dekoratorze. To komplikuje przetwarzanie, ponieważ należy uwzględnić dodatkowe poziomy dekoracji.

Proces będzie kontynuowany, aż otrzymamy „czysty” obiekt, który dokładnie określi swoje szanse przeżycia. Ten obiekt przekaże wartość do pierwszego dekoratora, który go wywoła. Dekorator zwiększy wartość zgodnie z własną implementacją i przekaże wynik z powrotem do obiektu wywołującego. Ten proces będzie powtarzany, aż wrócimy do punktu początkowego.

Rekurencja to ważna koncepcja w programowaniu i jeśli ją znasz, zauważyłeś już jej zastosowanie w tym algorytmie. Jeśli nie znasz rekurencji, zalecamy zapoznanie się z jej podstawami, aby lepiej zrozumieć mechanizmy działania algorytmów wykorzystujących to podejście. Rekurencja pozwala rozwiązywać złożone problemy poprzez rozbicie ich na prostsze podproblemy, dzięki czemu kod jest bardziej zwięzły i zrozumiały.

Poniższy rysunek przedstawia przykład obliczenia prawdopodobieństwa przeżycia pierwszego superbohatera z wcześniej wymienionej listy. Ta analiza pozwala ocenić szanse postaci w różnych sytuacjach, co może być przydatne przy tworzeniu scenariuszy i rozwijaniu fabuły.

Infografiki: Maya Malgina dla Skillbox Media

Szablon „Dekorator” pozwala Możesz dodawać nowe supermoce do klas bez konieczności ich zmiany. Takie podejście pozwala na łączenie różnych umiejętności i tworzenie postaci o wielu umiejętnościach. Na przykład, możesz zaprojektować superbohatera z podwójną superzwinnością, znacznie rozszerzając jego możliwości i czyniąc go bardziej wyjątkowym. Ten wzorzec sprawia, że ​​tworzenie i modyfikowanie postaci jest bardziej elastyczne i wydajne.

Jeśli logika Twojego programu zależy od konkretnych klas, użycie wzorca Dekorator może być nieskuteczne. Na przykład, jeśli zieloni superbohaterowie mają stać się bardziej zwinni niż czerwoni superbohaterowie w wyniku zastosowania superzwinności, preferowane jest standardowe dziedziczenie. Zapewni to prostszą i bardziej zrozumiałą architekturę kodu, zachowując jednocześnie niezbędną funkcjonalność.

Dekoratory działają z obiektami Superbohaterów, akceptując je i zwracając, bez uwzględniania specyficznych cech superbohaterów. W rezultacie, po zastosowaniu dekoratora, informacje o typie superbohatera (w tym przypadku o jego kolorze) zostają utracone. To ograniczenie należy uwzględnić podczas projektowania systemu dekoratorów, aby zachować ważne właściwości obiektów.

Podsumowując

Wzorzec projektowy Dekorator to skuteczne narzędzie do dynamicznego dodawania nowej funkcjonalności do obiektów. Stanowi doskonałą alternatywę dla dziedziczenia, zwłaszcza w sytuacjach, gdy nie ma potrzeby uwzględniania konkretnych typów obiektów dekorowanych w logice programu. Użycie tego wzorca zwiększa elastyczność i rozszerzalność kodu, umożliwiając zmianę zachowania obiektów bez konieczności ich modyfikowania. W ten sposób wzorzec Dekorator przyczynia się do powstania czystszej i łatwiejszej w utrzymaniu architektury rozwiązań programistycznych.

Na kursie „Java Developer PRO” poznasz różne wzorce projektowe, algorytmy, struktury danych i koncepcje programowania obiektowego z wykorzystaniem języka Java. Program kursu ma na celu opanowanie tego popularnego języka programowania i tworzenie wysokiej jakości aplikacji na różne platformy. Skillbox oferuje również wsparcie w znalezieniu pracy, które pomoże Ci z powodzeniem rozpocząć karierę w programowaniu w Javie.