Przejdź do treści
Strona główna » Blog » Wzorzec projektowy budowniczy

Wzorzec projektowy budowniczy

Kontynuując serię wpisów o wzorcach projektowych postanowiłem wziąć na warsztat wzorzec projektowy budowniczy. Wzorzec ten jest wzorcem kreacyjnym, który potrafi znacząco uprościć kod aplikacji, uczynić go bardziej przejrzystym, a jednocześnie sprawić, że dostajemy bardzo proste narzędzie do rozbudowy naszej aplikacji. Zapraszam do lektury.

Kiedy i Dlaczego właśnie budowniczy?

Wzorzec budowniczy można użyć wówczas, gdy musimy zaimplementować w naszej aplikacji tworzenie obiektów, które strukturalnie są do siebie bardzo podobne, lecz różnią się one implementacjami. Dodatkowo wzorzec ten umożliwia tworzenie obiektów w sposób etapowy. Najlepiej będzie, gdy spróbujemy przedstawić problem na prostym przykładzie jakim będzie tworzenie obiektu śruby.

Śruba wydaje się bardzo prostym przedmiotem lecz mnogość jej typów sprawia że nie łatwo jest stworzyć jedną klasę, która w prosty sposób dałaby radę opisać wiele jej rodzajów. Weźmy przykładowe cechy jakie może mieć jakaś śruba:

  • rozmiar gwintu,
  • typ gwintu,
  • długość gwintu,
  • długość całkowita,
  • kształt łba,
  • pokrycie,
  • jego rodzaj,
  • klasa wytrzymałości,
  • lewo lub prawoskrętność,
  • skok gwintu,
  • materiał z którego jest wykonana,
  • itd.

Gdybyśmy chcieli utworzyć przykładowy konstruktor naszej śruby, powstałby nam niezły potworek, który mógłby wyglądać np. tak.

 Sruba srubaPierwsza = new Sruba(6, "metryczna", 20, "walcowy z gniazdem sześciokątnym", "ocynk", "8.8", "prawoskretna", "normalny");

Jeden taki konstruktor można by przeboleć i przemęczyć się z wypełnianiem go, ale co zrobić gdy chcielibyśmy użyć wielu śrub, które różniłyby się tylko długością, np. M6x15, M6x20, M6x30. Najfajniej byłoby gdyby ktoś lub coś, wypełniłoby taki konstruktor za nas.

Zbudujmy budowniczego

Stwórzmy zatem klasę budowniczego naszych obiektów klasy śruba.

Klasa Śruba

package com.company;

public class Sruba {
    private double rozmiarGwintu;
    private String typ;
    private double dlugosc;
    private String typLba;
    private String pokrycie;
    private String klasa;
    private String skretnosc;
    private String skok;

   //setters and getters

    }

Klasa budowniczego

package com.company;

public class SrubaTyp1 {
    Sruba sruba;

    public SrubaTyp1(double rozmiar, double dlugosc){
        sruba = new Sruba();
        sruba.setRozmiarGwintu(rozmiar);
        sruba.setTyp("metryczna");
        sruba.setDlugosc(dlugosc);
        sruba.setTypLba("walcowy z gniazdem sześciokątnym");
        sruba.setPokrycie("ocynk");
        sruba.setKlasa("8.8");
        sruba.setSkretnosc("prawoskrestna");
        sruba.setSkok("normalny");
    }

    public Sruba getSruba() {
        return sruba;
    }
}

Zauważ, że teraz aby stworzyć wiele śrub typu 1 wystarczy tylko stworzyć obiekt budowniczego śruby typu 1 oraz obiekt klasy Sruba do którego przypiszemy rezultat pracy budowniczego jak na poniższym przykładzie:

package com.company;

public class Main {

    public static void main(String[] args) {
        SrubaTyp1 budowniczy1 = new SrubaTyp1(6,20);
        Sruba sruba1 = budowniczy1.getSruba();

        budowniczy1.setDlugoscSrubby(25); //zmieniam tylko długość
        Sruba sruba2 = budowniczy1.getSruba(); //tworzę kolejną śrubę o nowej dlugosci

    }
}

Zobacz jak eleganckie rozwiązanie otrzymaliśmy. Zamiast tworzyć wiele długich, mało czytelnych konstruktorów stworzyliśmy klasę, która tworzy obiekty klasy Sruba za nas. Analogicznie do klasy SrubaTyp1 można tworzyć kolejne klasy SrubaTyp2, SrubaTyp3 itd. Aby zapewnić spójność i prawidłowe tworzenie obiektów klasy Sruba stwórzmy interfejs, który będzie musiał być zaimplementowany w każdej klasie budowniczego klasy Sruba.

package com.company;

public interface SrubaTypy {
    public void setRozmiarSruby(double rozmiarSruby);

    public void setTyp(String typ);

    public void setDlugosc(double dlugosc);

    public void setTypLba(String typLba);

    public void setPokrycie(String pokrycie);

    public void setKlasa(String klasa);

    public void setSkretnosc(String skretnosc);

    public void setSkok(String skok);
}

Mając taki interfejs zmieniam klasę SrubaTyp1, tak aby implementowała zapisany powyżej interfejs:

package com.company;

public class SrubaTyp1 implements SrubaTypy {
    Sruba sruba;

    public SrubaTyp1(double rozmiar, double dlugosc) {
        sruba = new Sruba();
        setRozmiarSruby(rozmiar);
        setTyp("metryczna");
        setDlugosc(dlugosc);
        setTypLba("walcowy z gniazdem sześciokątnym");
        setPokrycie("ocynk");
        setKlasa("8.8");
        setSkretnosc("prawoskrestna");
        setSkok("normalny");
    }

    public Sruba getSruba() {
        return sruba;
    }

    @Override
    public void setRozmiarSruby(double rozmiar) {
        sruba.setRozmiarGwintu(rozmiar);
    }

    @Override
    public void setTyp(String typ) {
        sruba.setTyp(typ);
    }

    @Override
    public void setDlugosc(double dlugosc) {
        sruba.setDlugosc(dlugosc);
    }

    @Override
    public void setTypLba(String typLba) {
        sruba.setTypLba(typLba);
    }

    @Override
    public void setPokrycie(String pokrycie) {
        sruba.setPokrycie("ocynk");
    }

    @Override
    public void setKlasa(String klasa) {
        sruba.setKlasa(klasa);
    }

    @Override
    public void setSkretnosc(String skretnosc) {
        sruba.setSkretnosc(skretnosc);
    }

    @Override
    public void setSkok(String skok) {
        sruba.setSkok(skok);
    }
}

Zauważ, że teraz mamy sytuację, w której już na etapie tworzenia obiektu budowniczego konstruktor tworzy nam obiekt Śruby i automatycznie wypełnia wszystkie pola. Oprócz tego mamy możliwość dowolnej zmiany wartości pól Sruby poprzez settery w klasie budowniczego. W obecnej sytuacji to budowniczy sam nadzoruje proces kreowania śruby.

Postawmy nad budowniczym kierownika

We wzorcu budowniczy często stosuje się dodatkową klasę kierownika, która czuwa nad etapami i kolejnością wykorzystania metod z klasy budowniczego. Takie podejście sprawia że kod staje się bardziej przejrzysty i łatwiejszy w operowaniu budowniczymi. Zapiszmy poniżej przykładowy kod kierownika:

package com.company;

public class Kierownik {

    public void stworzSrubeTypuPierwszego(SrubaTypy budowniczySruby) {
        budowniczySruby.setDlugosc(20);
        budowniczySruby.setRozmiarSruby(6);
        budowniczySruby.setTypLba("Stożkowy z gniazdem sześciokątnym");
    }

    public void stworzSrubeTypuDrugiego(SrubaTypy budowniczySruby) {
        budowniczySruby.setDlugosc(30);
        budowniczySruby.setKlasa("8.8");
        budowniczySruby.setRozmiarSruby(8);
    }
}

Mając takiego kierownika, który instruuje jak ma towrzyć obiekt klasy śruba można odchudzić konstruktor budowniczego i wówczas klasa będzie wyglądać przykładowo tak:

package com.company;

public class SrubaTyp1 implements SrubaTypy {
    Sruba sruba;

    public SrubaTyp1(double rozmiar, double dlugosc) {
        sruba = new Sruba();
    }

    public Sruba getSruba() {
        return sruba;
    }

    @Override
    public void setRozmiarSruby(double rozmiar) {
        sruba.setRozmiarGwintu(rozmiar);
    }

    @Override
    public void setTyp(String typ) {
        sruba.setTyp(typ);
    }

    @Override
    public void setDlugosc(double dlugosc) {
        sruba.setDlugosc(dlugosc);
    }

    @Override
    public void setTypLba(String typLba) {
        sruba.setTypLba(typLba);
    }

    @Override
    public void setPokrycie(String pokrycie) {
        sruba.setPokrycie("ocynk");
    }

    @Override
    public void setKlasa(String klasa) {
        sruba.setKlasa(klasa);
    }

    @Override
    public void setSkretnosc(String skretnosc) {
        sruba.setSkretnosc(skretnosc);
    }

    @Override
    public void setSkok(String skok) {
        sruba.setSkok(skok);
    }
}

Zyskaliśmy lżejszy konstruktor klasy budowniczego, a także wyodrębniliśmy metodykę tworzenia śruby do osobnej klasy, co dało nam przejrzystość i łatwość w budowaniu kolejnych implementacji różnych typów śruby w jednym miejscu poprzez dodawanie nowych metod w klasie kierownika. Dodatkowo w tym momencie umożliwiliśmy uzytkownikowi zaimplementowanie własnej kolejności budowania obiektu, a dodatkowo możemy teraz w klasie kierownika wprowadzić np. instrukcje warunkowe, które będą nadzorowały kolejne etapy tworzenia obiektu śruby.

Aby skorzystać z tak utworzonej struktury klas przebudujmy zatem nasz kod kliencki:

package com.company;

public class Main {

    public static void main(String[] args) {
        Kierownik kierownik = new Kierownik(); // zatrudniam do pracy kierownika
        SrubaTypy budowniczySrubyPierwszy = new SrubaTyp1(); // zatrudniam pierwszego budowniczego
        SrubaTypy budowniczySrubyDrugi = new SrubaTyp1(); // zatrudniam drugiego budowniczego

        kierownik.stworzSrubeTypuPierwszego(budowniczySrubyPierwszy); //przypisuję do stowrzenia sruby konkretnego budowniczego
        kierownik.stworzSrubeTypuDrugiego(budowniczySrubyDrugi); //przypisuję do stowrzenia sruby konkretnego budowniczego

        Sruba pierwszaSruba = budowniczySrubyPierwszy.getSruba(); // przypisuje do pierwszaSruba efekt pracy budowniczego pierwszego
        Sruba drugaSruba = budowniczySrubyDrugi.getSruba(); // przypisuje do drugaSruba efekt pracy budowniczego drugiego

        System.out.println("---SRUBA PIERWSZA---");
        System.out.println("Rozmiar gwintu: " + pierwszaSruba.getRozmiarGwintu());
        System.out.println("Typ: " + pierwszaSruba.getTyp());
        System.out.println("Dlugosc: " + pierwszaSruba.getDlugosc());
        System.out.println("Typ Lba: " + pierwszaSruba.getTypLba());
        System.out.println("Pokrycie: " + pierwszaSruba.getPokrycie());
        System.out.println("Klasa: " + pierwszaSruba.getKlasa());
        System.out.println("Skretnosc: " + pierwszaSruba.getSkretnosc());
        System.out.println("Skok: " + pierwszaSruba.getSkok());

        System.out.println("---SRUBA DRUGA---");
        System.out.println("Rozmiar gwintu: " + drugaSruba.getRozmiarGwintu());
        System.out.println("Typ: " + drugaSruba.getTyp());
        System.out.println("Dlugosc: " + drugaSruba.getDlugosc());
        System.out.println("Typ Lba: " + drugaSruba.getTypLba());
        System.out.println("Pokrycie: " + drugaSruba.getPokrycie());
        System.out.println("Klasa: " + drugaSruba.getKlasa());
        System.out.println("Skretnosc: " + drugaSruba.getSkretnosc());
        System.out.println("Skok: " + drugaSruba.getSkok());

    }
}

Efekt uruchomienia programu będzie następujący:

---SRUBA PIERWSZA---
Rozmiar gwintu: 6.0
Typ: null
Dlugosc: 20.0
Typ Lba: Stożkowy z gniazdem sześciokątnym
Pokrycie: null
Klasa: null
Skretnosc: null
Skok: null
---SRUBA DRUGA---
Rozmiar gwintu: 8.0
Typ: null
Dlugosc: 30.0
Typ Lba: null
Pokrycie: null
Klasa: 8.8
Skretnosc: null
Skok: null

Process finished with exit code 0

Podsumowanie

Wzorzec budowniczy jest średnio skomplikowanym wzorcem projektowym, ale mądrze użyty dodaje aplikacji elastyczności i umożliwia programiście etapowe tworzenie obiektów. Dodatkowo kod kliencki znacząco upraszcza się i jest bardziej przejrzysty, co wynika chociażby z braku potrzeby tworzenia skomplikowanych i rozległych konstruktorów.

Zasadniczą wadą tego wzorca jest to że wymaga stworzenia dodatkowych klas i interfejsów, co w niektórych projektach może niepotrzebnie skomplikować jego strukturę klasową.

W celu świadczenia usług na najwyższym poziomie stosuję pliki cookies, które będą zamieszczane w Państwa urządzeniu (komputerze, laptopie, smartfonie). W każdym momencie mogą Państwo dokonać zmiany ustawień Państwa przeglądarki internetowej i wyłączyć opcję zapisu plików cookies. Ze szczegółowymi informacjami dotyczącymi cookies na tej stronie można się zapoznać tutaj: View more
Ustawienia ciasteczek
Akceptuj
Blokuj
Polityka prywatności
Polityka prywatności & ciasteczka
Nazwa ciasteczka Aktywny

Polityka prywatności opisuje zasady przetwarzania przezE MNIE informacji na Twój temat, w tym danych osobowych oraz ciasteczek, czyli tzw. cookies.


1. Informacje ogólne

  1. Niniejsza polityka dotyczy Serwisu www, funkcjonującego pod adresem url: www.wojciechsiwek.pl
  2. Operatorem serwisu oraz Administratorem danych osobowych jest: Wojciech Siwek
  3. Adres kontaktowy poczty elektronicznej operatora: wojciech.siwek.programista@gmail.com
  4. Operator jest Administratorem Twoich danych osobowych w odniesieniu do danych podanych dobrowolnie w Serwisie.
  5. Serwis wykorzystuje dane osobowe w następujących celach:
    • Prowadzenie systemu komentarzy
    • Obsługa zapytań przez formularz
  6. Serwis realizuje funkcje pozyskiwania informacji o użytkownikach i ich zachowaniu w następujący sposób:
    1. Poprzez dobrowolnie wprowadzone w formularzach dane, które zostają wprowadzone do systemów Operatora.
    2. Poprzez zapisywanie w urządzeniach końcowych plików cookie (tzw. „ciasteczka”).

2. Wybrane metody ochrony danych stosowane przez Operatora

  1. Miejsca logowania i wprowadzania danych osobowych są chronione w warstwie transmisji (certyfikat SSL). Dzięki temu dane osobowe i dane logowania, wprowadzone na stronie, zostają zaszyfrowane w komputerze użytkownika i mogą być odczytane jedynie na docelowym serwerze.
  2. Hasła użytkowników są przechowywane w postaci hashowanej. Funkcja hashująca działa jednokierunkowo - nie jest możliwe odwrócenie jej działania, co stanowi obecnie współczesny standard w zakresie przechowywania haseł użytkowników.
  3. Operator okresowo zmienia swoje hasła administracyjne.
  4. W celu ochrony danych Operator regularnie wykonuje kopie bezpieczeństwa.
  5. Istotnym elementem ochrony danych jest regularna aktualizacja wszelkiego oprogramowania, wykorzystywanego przez Operatora do przetwarzania danych osobowych, co w szczególności oznacza regularne aktualizacje komponentów programistycznych.

3. Hosting

  1. Serwis jest hostowany (technicznie utrzymywany) na serwera operatora: NETMARK.

4. Twoje prawa i dodatkowe informacje o sposobie wykorzystania danych

  1. W niektórych sytuacjach Administrator ma prawo przekazywać Twoje dane osobowe innym odbiorcom, jeśli będzie to niezbędne do wykonania zawartej z Tobą umowy lub do zrealizowania obowiązków ciążących na Administratorze. Dotyczy to takich grup odbiorców:
    • operatorzy pocztowi
    • operatorzy systemu komentarzy
    • upoważnieni pracownicy i współpracownicy, którzy korzystają z danych w celu realizacji celu działania strony
  2. Twoje dane osobowe przetwarzane przez Administratora nie dłużej, niż jest to konieczne do wykonania związanych z nimi czynności określonych osobnymi przepisami (np. o prowadzeniu rachunkowości). W odniesieniu do danych marketingowych dane nie będą przetwarzane dłużej niż przez 3 lata.
  3. Przysługuje Ci prawo żądania od Administratora:
    • dostępu do danych osobowych Ciebie dotyczących,
    • ich sprostowania,
    • usunięcia,
    • ograniczenia przetwarzania,
    • oraz przenoszenia danych.
  4. Przysługuje Ci prawo do złożenia sprzeciwu w zakresie przetwarzania wskazanego w pkt 3.3 c) wobec przetwarzania danych osobowych w celu wykonania prawnie uzasadnionych interesów realizowanych przez Administratora, w tym profilowania, przy czym prawo sprzeciwu nie będzie mogło być wykonane w przypadku istnienia ważnych prawnie uzasadnionych podstaw do przetwarzania, nadrzędnych wobec Ciebie interesów, praw i wolności, w szczególności ustalenia, dochodzenia lub obrony roszczeń.
  5. Na działania Administratora przysługuje skarga do Prezesa Urzędu Ochrony Danych Osobowych, ul. Stawki 2, 00-193 Warszawa.
  6. Podanie danych osobowych jest dobrowolne, lecz niezbędne do obsługi Serwisu.
  7. W stosunku do Ciebie mogą być podejmowane czynności polegające na zautomatyzowanym podejmowaniu decyzji, w tym profilowaniu w celu świadczenia usług w ramach zawartej umowy oraz w celu prowadzenia przez Administratora marketingu bezpośredniego.
  8. Dane osobowe nie są przekazywane od krajów trzecich w rozumieniu przepisów o ochronie danych osobowych. Oznacza to, że nie przesyłamy ich poza teren Unii Europejskiej.

5. Informacje w formularzach

  1. Serwis zbiera informacje podane dobrowolnie przez użytkownika, w tym dane osobowe, o ile zostaną one podane.
  2. Serwis może zapisać informacje o parametrach połączenia (oznaczenie czasu, adres IP).
  3. Serwis, w niektórych wypadkach, może zapisać informację ułatwiającą powiązanie danych w formularzu z adresem e-mail użytkownika wypełniającego formularz. W takim wypadku adres e-mail użytkownika pojawia się wewnątrz adresu url strony zawierającej formularz.
  4. Dane podane w formularzu są przetwarzane w celu wynikającym z funkcji konkretnego formularza, np. w celu dokonania procesu obsługi zgłoszenia serwisowego lub kontaktu handlowego, rejestracji usług itp. Każdorazowo kontekst i opis formularza w czytelny sposób informuje, do czego on służy.

6. Logi Administratora

  1. Informacje zachowaniu użytkowników w serwisie mogą podlegać logowaniu. Dane te są wykorzystywane w celu administrowania serwisem.

7. Istotne techniki marketingowe

  1. Operator stosuje analizę statystyczną ruchu na stronie, poprzez Google Analytics (Google Inc. z siedzibą w USA). Operator nie przekazuje do operatora tej usługi danych osobowych, a jedynie zanonimizowane informacje. Usługa bazuje na wykorzystaniu ciasteczek w urządzeniu końcowym użytkownika. W zakresie informacji o preferencjach użytkownika gromadzonych przez sieć reklamową Google użytkownik może przeglądać i edytować informacje wynikające z plików cookies przy pomocy narzędzia: https://www.google.com/ads/preferences/

8. Informacja o plikach cookies

  1. Serwis korzysta z plików cookies.
  2. Pliki cookies (tzw. „ciasteczka”) stanowią dane informatyczne, w szczególności pliki tekstowe, które przechowywane są w urządzeniu końcowym Użytkownika Serwisu i przeznaczone są do korzystania ze stron internetowych Serwisu. Cookies zazwyczaj zawierają nazwę strony internetowej, z której pochodzą, czas przechowywania ich na urządzeniu końcowym oraz unikalny numer.
  3. Podmiotem zamieszczającym na urządzeniu końcowym Użytkownika Serwisu pliki cookies oraz uzyskującym do nich dostęp jest operator Serwisu.
  4. Pliki cookies wykorzystywane są w następujących celach:
    1. utrzymanie sesji użytkownika Serwisu (po zalogowaniu), dzięki której użytkownik nie musi na każdej podstronie Serwisu ponownie wpisywać loginu i hasła;
    2. realizacji celów określonych powyżej w części "Istotne techniki marketingowe";
  5. W ramach Serwisu stosowane są dwa zasadnicze rodzaje plików cookies: „sesyjne” (session cookies) oraz „stałe” (persistent cookies). Cookies „sesyjne” są plikami tymczasowymi, które przechowywane są w urządzeniu końcowym Użytkownika do czasu wylogowania, opuszczenia strony internetowej lub wyłączenia oprogramowania (przeglądarki internetowej). „Stałe” pliki cookies przechowywane są w urządzeniu końcowym Użytkownika przez czas określony w parametrach plików cookies lub do czasu ich usunięcia przez Użytkownika.
  6. Oprogramowanie do przeglądania stron internetowych (przeglądarka internetowa) zazwyczaj domyślnie dopuszcza przechowywanie plików cookies w urządzeniu końcowym Użytkownika. Użytkownicy Serwisu mogą dokonać zmiany ustawień w tym zakresie. Przeglądarka internetowa umożliwia usunięcie plików cookies. Możliwe jest także automatyczne blokowanie plików cookies Szczegółowe informacje na ten temat zawiera pomoc lub dokumentacja przeglądarki internetowej.
  7. Ograniczenia stosowania plików cookies mogą wpłynąć na niektóre funkcjonalności dostępne na stronach internetowych Serwisu.
  8. Pliki cookies zamieszczane w urządzeniu końcowym Użytkownika Serwisu wykorzystywane mogą być również przez współpracujące z operatorem Serwisu podmioty, w szczególności dotyczy to firm: Google (Google Inc. z siedzibą w USA), Facebook (Facebook Inc. z siedzibą w USA), Twitter (Twitter Inc. z siedzibą w USA).

9. Zarządzanie plikami cookies – jak w praktyce wyrażać i cofać zgodę?

  1. Jeśli użytkownik nie chce otrzymywać plików cookies, może zmienić ustawienia przeglądarki. Zastrzegamy, że wyłączenie obsługi plików cookies niezbędnych dla procesów uwierzytelniania, bezpieczeństwa, utrzymania preferencji użytkownika może utrudnić, a w skrajnych przypadkach może uniemożliwić korzystanie ze stron www
  2. W celu zarządzania ustawienia cookies wybierz z listy poniżej przeglądarkę internetową, której używasz i postępuj zgodnie z instrukcjami:
Zapisz ustawienia
Ustawienia ciasteczek