Skip to content

Witaj w Świecie Jutra!

  • Technologie jutra
  • Sprzęt jutra
  • Aplikacje jutra
  • Programowanie
  • Księga Drogi
  • Renowacja
  • Różności
  • Archiwum
  • Autor
  • Home
  • Wszystko
  • Wzorce projektowe – Builder
  • Programowanie
  • Wszystko

Wzorce projektowe – Builder

Jakub Raczkowski 31 stycznia 2024

Bob Budowniczy - Zawsze da radę!

Wbrew pozorom branża IT ma zadziwiająco dużo wspólnego z budownictwem. Pierwsze metodologie tworzenia architektury oprogramowania wyglądały łudząco podobnie do planów stawiania budynków – idziemy od fundamentów w górę aż uda się położyć dach. Dość powiedzieć, że to właśnie z budowlanki pochodzi nazwa „konstruktor”.

Kiedy zacząłem swoją przygodę z programowaniem dużo się głowiłem nad (z dzisiejszego punktu widzenia) banałami. Np. takimi jak:

Czym właściwie jest konstruktor?

Jeśli teraz miałbym odpowiedzieć na to pytanie najprościej jak to możliwe to użyłbym właśnie porównania do placu budowy. Mamy tam cegły, piasek i cement – tyle ile chcemy. To litery z których możemy napisać taki kod jaki jest nam potrzebny. Z nich zrobimy fundamenty, ściany, schody i strop – podstawowe elementy każdej chałupy.

Ale przyznacie, że w takim czymś trudno jeszcze zamieszkać. Przydałyby się drzwi i okna, kanalizacja, elektryka, dach… Tego sami nie możemy zrobić bo to inna branża. Musimy posiłkować się obiektami innych klas. Niektóre z wymienionych rzeczy są niezbędne – plan konstrukcji (czyli konstruktor) musi to uwzględnić – jeśli np. nie będziemy mieli dachu to nawet nie ma co zaczynać budowy.

Obiekt „Dach” po prostu MUSI się znaleźć w parametrach konstruktora!

Inne części domu mogą być opcjonalne, dajmy na to kominek. Bez nich plan konstrukcji się obejdzie, co najwyżej dostawimy je potem używając settera.

public void setFireplace(Fireplace fireplace){
this.fireplace = fireplace
}
I cyk, kominek już jest! Źródło obrazka.

Czasami zdarza się, że tych obowiązkowych parametrów jest za dużo. Wtedy możemy wspomóc się przeładowaniem konstruktora wraz z użyciem wartości domyślnych:

public House (Roof roof){
	this.roof = roof;
	heating = new RegularHeating();
	electricity = new Standard Electricity();
	plumbing = new BasicPlumbing();
}

Mając taki konstruktor jak powyżej wszystkie niezbędne elementy są ustawione, ale pojawia się też pokusa użycia go do zbudowania szopki narzędziowej, której wystarczy dach i elektryka, a resztę wartości można ustawić na „null”. Więc warto stworzyć osobny konstruktor (mówi się: „przeładować” (ang. overload)). A potem jeszcze jeden dla domku letniskowego i dla budy dla psa i dla…

Klasa (w żółtym) i jej builder (w czerwonym). Źródło: Sąsiedzi Pat & Mat

Widzicie już do czego to zmierza? Konstruktory mnożą się jak króliki, a kod staje się nieczytelny. Aby zaradzić temu problemowi wymyślono wzorzec „Budowniczy” (Builder):

Builder – hermetyzuje operacje niezbędne do stworzenia złożonego obiektu ukrywając ich wewnętrzną strukturę przed klientem. Umożliwia tworzenie obiektów w procedurze wielokrokowej i nie narzuca tej procedury.

Rusz głową! Wzorce projektowe

Ale po kolei…

Jednym z najczęściej spotykanych przeciwników w światach fantasy są Orkowie. Były we Władcy pierścieni, w Warcrafcie, Warhammerze, DnD, Gothicu, grach z serii DivineDivinity czy Heroes. Ogólny opis orka jest powszechnie znany: silny ale niezbyt bystry, agresywny i zielony. Kiedy jednak padnie pytanie:

Czy Orkowie to trudni przeciwnicy?

To pojawia się dylemat. Z jednej strony mamy zwykłych piechurów, którzy padają od strzału. Z drugiej orkowi watażkowie często pełnią rolę minibossów. Są Orkowie łucznicy, beznadziejni w zwarciu ale zabójczy na dystans. Są szamani wspomagający swoje jednostki. Są jeźdźcy dzików, tarczownicy, kamikadze, władcy zwierząt. Nawet w świecie programowania są (framew)Orki 😉

Neo-ork z karabinem. Warhammer 40k

Ogólnie rzecz biorąc – dużo ich jest. I teraz pomyślmy jak ich wszystkich zakodować w naszej grze. Oczywiście możemy zrobić abstrakcyjnego orka i po nim dziedziczyć, ale ilość powstałych typów nas szybko przytłoczy. Już nie mówiąc nawet o masowej duplikacji kodu. Nie tędy droga.

Z pomocą przychodzi Builder

Jako, że Builder w założeniu jest klasą „nested” czyli zagnieżdżoną (statyczną klasą w innej klasie) stworzyłem dwa pliki z kodem. Pierwszy (tutaj) pokazuje jak Budowniczy działa, a drugi (tutaj) jak wygląda jego implementacja.

Trzy orki, każdy inny ale każdy pochodzi z tej samej klasy.

Jak widzicie, stworzyłem trzech orków: orc, orcArcher, orcChieftain. Pierwszy jest implementacją domyślną – jego parametry zostawiłem takie jak ustawione w klasie i wyszedł mi podstawowy model.

Domyślne statystyki zwykłego orka mogą z łatwością zostać zmienione.

Nazwa klasy „budującej” orki to OrcBuilder który tworzy swój obiekt i „wstrzykuje” go do konstruktora klasy Orc. Jest ustawiony jako prywatny, aby tylko Builder miał do niego dostęp:

private Orc(OrcBuilder orcBuilder) {
            this.strength = orcBuilder.strength;
            this.hp = orcBuilder.hp;
            this.attackSpeed = orcBuilder.attackSpeed;
            this.rank = orcBuilder.rank;
            this.attack = orcBuilder.attack;
        }

Konstruktor ma aż 5 potrzebnych parametrów, dlatego lepiej unikać jego przeładowania i użyć Buildera. Ten albo stworzy obiekt domyślny, albo pozwoli klientowi na samodzielne ustawienie pożądanych elementów – od tego są settery:

Settery są inne od tych tworzonych automatycznie przez IntelliJ. Zwracają swój obiekt.

Każdy setter zwraca swój obiekt OrcBuilder (return this;) dlatego mogą się na siebie nakładać. Kiedy budowa jest już zaplanowana, kończymy ją pleceniem build() zwracającym nam gotowego Orka.

public Orc build(){
            return new Orc(this);
        }

Oczywiście nic nie stoi na przeszkodzie, aby zrobić schemat Buildera pod konkretne typy, tak aby nie musieć za każdym razem robić tego ręcznie. Podklasa OrkChieftain zawiera tylko metodę build() uruchamiającą wszystkie wybrane settery:

Orka-szefa tworzy się łatwo, ma osobną podklasę.

Podsumowując:

  • Wzorca Builder używamy wtedy kiedy konstruktor danej klasy przyjmuje za dużo parametrów (> 3).
  • Aby stworzyć prostego budowniczego piszemy klasę zagnieżdzoną (static class) wewnątrz docelowej klasy.
  • Konstruktor docelowej klasy robimy prywatnym i użależniamy go tylko od obiektu Buildera.
  • Klasa Builder ma mieć zainicjalizowane te same parametry co klasa docelowa, warto im nadać wartości domyślne.
  • Każdy z tych parametrów musi mieć specjalny setter zwracający obiekt Buildera.
  • Ostatnia metoda Buildera to build() która zwraca obiekt klasy docelowej poprzez użycie jej konstruktora z wstrzykniętym własnym obiektem.

ITCandidateEvaluator

Spośród dodatkowych wzorców, pokrótce omówionych na samym końcu podręcznika Rusz Głową! nieprzypadkowo wybrałem właśnie Buildera. Myślę, że świetnie nada się do tworzenia obiektów Rekrutacja na samym początku aplikacji. Chciałbym dać użytkownikowi wybór jakie elementy rekrutacji pojawią się w jej trakcie – np. będzie mógł zaznaczyć czy chce odpytywać kandydata ze znajomości języka angielskiego, albo czy przewiduje tzw. „live coding„. Tych parametrów będzie więcej i uważam, że Builder najlepiej sprawdzi się w ich zarządzaniu.

Do końca tego tygodnia planuję jeszcze opisanie dwóch wzorców. Następny będzie Chain of Command (Łańcuch zarządzania), a po nim wezmę się za wzorzec złożony Model-View-Controller. Kolejny krokiem będzie już praca nad moim drugim projektem.

Do omówienia zostaną jeszcze takie wzorce jak:

  • Bridge
  • Flyweight
  • Interpreter
  • Mediator
  • Memento
  • Prototype
  • Visitor

Szybka lektura opisów ich zastosowań przekonała mnie, że nie są aż tak kluczowe jak te, którymi zajmowaliśmy się do tej pory. Być może kiedyś jeszcze wrócę do tematu, ale teraz są ważniejsze rzeczy do wykonania.

Jak zwykle – dziękuję za uwagę i zapraszam do następnych artykułów.

Tags: design patterns programming

Continue Reading

Previous: Wzorce projektowe – Proxy
Next: Wzorce projektowe – Chain of Responsibility

Related Stories

Mageege Moon104 – test niskoprofilowego mechanika
  • Sprzęt

Mageege Moon104 – test niskoprofilowego mechanika

11 marca 2025
Przebranżowienie cz.4
  • Programowanie

Przebranżowienie cz.4

27 lutego 2025
Smartfon Jutra
  • Sprzęt

Smartfon Jutra

15 lutego 2025

Ze świata

  • Antyweb
  • Kwantowo
  • Dwóch po dwóch
Netflix odpala nową funkcję, która da użytkownikom więcej władzy
Tanie iPhone'y i Samsungi w nowym sklepie Orange
Fani Androida mają powód do radości. Wraca wsparcie uwielbianej funkcji
Wielkie nieporozumienie wokół nowego iPhone’a. Zobaczcie to, zanim zaczniecie krytykować
Chiny przejmują legendarny oddział Sony. Koniec ery
Tak chcemy wyjaśnić napięcie Hubble'a. Lepszej metody dotąd nie było
Netflix odkrywa karty o premierach. Wiemy, co pokaże na dniach
A co powiesz na ładowanie... laserem? Badacze już to testują
ChatGPT zabierze nasze pieniądze? Nowy pomysł jeszcze gorszy niż reklamy
To najmniejsza myszka na świecie. Użyjesz jej wszędzie
Ta aplikacja to „centrum dowodzenia” każdego miłośnika podróży
Kaufland zaszalał. Rozchwytywany sprzęt Parkside trafił na półki
O tych nowościach w Windows 11 jest dziwnie cicho. A szkoda
Microsoft szykuje rewolucję. Skorzystasz, jeśli obejrzysz reklamy
Te zestawy LEGO za moment znikną z rynku. Wiecie, co to oznacza?
To już ostatni dzwonek! Skorzystaj za darmo, zanim Apple to wyłączy
Masz uczulenie na sierść psa? Z tym nic ci nie grozi
Prawdziwa bomba od T-Mobile - klienci Heyah będą zachwyceni!
Garmin, Samsung i Apple na celowniku. Te zegarki mogą zniknąć z rynku
Łatwiej zmienisz przeglądarkę na iPhonie. Wystarczy jedna opcja
To by było na tyle, jeśli chodzi o możliwość ugody
Ocalić od zapomnienia
Ostatni kwant
ALH 84001 – meteoryt, o którym mówiono nawet w Białym Domu
HESS zarejestrował kosmiczny elektron o niespotykanej energii [Phys. Rev. Lett.]
Matka ciemnej materii – recenzja biografii “Vera Rubin. Życie”
Satelita, który zerwał się ze smyczy
Wiadomość od Carla Sagana do przyszłych eksploratorów Marsa
Ile najdłużej może trwać zaćmienie Słońca?
Nowa największa liczba pierwsza ma ponad 41 milionów cyfr [GIMPS]
30 lat konsoli PlayStation – Odcinek #130
Omawiamy serię The Walking Dead (gość: Stary Gracz)
Nikt nie potrzebuje cienkich smartfonów – Odcinek #129
To ostatni dzwonek na kolekcjonowanie gier i filmów
Najlepsza relacja z PGA 2025 (Poznań Game Arena)
Bumblebee wśród klawiatur. Marvo Meqa 80W – recenzja
Pierwsze spotkanie z Omoda 7 Super Hybrid
Logitech MX Master 4, Wednesday, 1670 sezon 2 – Odcinek #128
Tani pad, który chciał być jak DualSense. Test Monka Contra GT-96
Secret Service i prasa komputerowa w Polsce – Odcinek #127

To może cię zainteresować:

Mageege Moon104 – test niskoprofilowego mechanika
  • Sprzęt

Mageege Moon104 – test niskoprofilowego mechanika

11 marca 2025
Przebranżowienie cz.4
  • Programowanie

Przebranżowienie cz.4

27 lutego 2025
Smartfon Jutra
  • Sprzęt

Smartfon Jutra

15 lutego 2025
Czym jest Swagger?
  • Programowanie

Czym jest Swagger?

22 lipca 2024
  • Technologie jutra
  • Sprzęt jutra
  • Aplikacje jutra
  • Programowanie
  • Księga Drogi
  • Renowacja
  • Różności
  • Archiwum
  • Autor
Copyright © All rights reserved. | DarkNews by AF themes.