Pierwszym wzorcem który opiszę w serii artykułów o wzorcach projektowych będzie wzorzec budowniczy (ang. Builder). Na sam początek przyjrzymy się definicji wzorca.
„Wzorca Builder używamy do hermetyzowania tworzenia produktu w celu jego wieloetapowego inicjowania.”
Eric Freeman, Elisabeth Freeman, Bert Bates, Kathy Sierra – „Rusz głową! Wzorce Projektowe”
Mówiąc inaczej wzorzec ten pozwala nam tworzyć skomplikowane obiekty w poszczególnych krokach. Zależnie od specyfikacji obiektu możemy pominąć poszczególne kroki. Część konstrukcyjna wydzielona jest w osobnych obiektach zwanych budowniczymi, a więc za pośrednictwem wywołania obiektów budowniczych możemy tworzyć dany produkt. Przyjrzymy się diagramowi klas tego wzorca.

Klasy budowniczych implementują interfejs budowniczego. Klient ma dostęp tylko do klasy budowniczego ( bądź do dyrektora), a zatem reprezentacja produktu jest ukryta przed klientem. Widzimy też opcjonalną klasę dyrektora, który może decydować o kolejności i części wykorzystania poszczególnych kroków tworzenia obiektu. Wzorzec ten jest najczęściej stosowany do tworzenia złożonych obiektów, celem uniknięcia tworzenia bardzo rozbudowanych konstruktorów obiektów oraz hermetyzacji części konstrukcyjnej obiektu. Często porównywany do wzorca fabryki, różni się tym iż tutaj posiadamy wieloetapową procedure powstawania obiektu w porównaniu do jednokrokowej narzuconej z góry procedury wzorca fabryki.
Przyjrzymy się prostemu przykładowi wykorzystania wzorca Builder. Wyobraźmy sobie, że jesteśmy firmą stolarską produkującą różne meble oraz oraz usługi stolarskie. W firmie tworzymy kilka rodzajów stołów dla klientów. W poniższym przykładzie pokazano jak za pomocą klas budowniczych oraz dyrektora który odpowiednio „wydaje polecenia” do tworzenia konkretnego stołu można wykorzystać wzorzec. Klasa dyrektora tak jak wcześniej wspomniano jest opcjonalną klasą którą postanowiłem pokazać.
- Pierwszym etapem jest stworzenie interfejsu budowniczego.
public interface Budowniczy {
void setWysokosc(int wysokosc);
void setSzerokosc(int szerokosc);
void setDlugosc(int dlugosc);
void setSrednica(int srednica);
void setTyp(String typ);
void setMaterial(String material);
void setNazwa(String nazwa);
void setCzyRozkladany(boolean czyRozkladany);
Stol getStol();
}
2. Kolejnym stworzenie prawdziwego budowniczego implementującego interfejs Budowniczy.
public class BudowniczyStolu implements Budowniczy{
private Stol stol = new Stol();
@Override
public void setWysokosc(int wysokosc) {
stol.setWysokosc(wysokosc);
}
@Override
public void setSzerokosc(int szerokosc) {
stol.setSzerokosc(szerokosc);
}
@Override
public void setDlugosc(int dlugosc) {
stol.setDlugosc(dlugosc);
}
@Override
public void setSrednica(int srednica) {
stol.setSrednica(srednica);
}
@Override
public void setTyp(String typ) {
stol.setTyp(typ);
}
@Override
public void setMaterial(String material) {
stol.setMaterial(material);
}
@Override
public void setNazwa(String nazwa) {
stol.setNazwa(nazwa);
}
@Override
public void setCzyRozkladany(boolean czyRozkladany) {
stol.setCzyRozkladany(czyRozkladany);
}
@Override
public Stol getStol() {
return stol;
}
}
3. Następnie tworzymy dyrektora który zarządza procesem powstawania różnego rodzaju stołów.
public class Dyrektor {
public void stworzOkraglyStol(Budowniczy budowniczy){
System.out.println("---Wydaje polecenia budowniczemu do stworzenia okraglego stolu---");
System.out.println("Budowniczy pracuje...");
budowniczy.setNazwa("Stół okrągły nierozkladany DANIEL");
budowniczy.setTyp("okragly");
budowniczy.setSrednica(600);
budowniczy.setWysokosc(90);
budowniczy.setMaterial("Drzewo debowe");
budowniczy.setCzyRozkladany(false);
System.out.println("---Stół okragly gotowy---");
}
public void stworzProstokatnyStol(Budowniczy budowniczy){
System.out.println("---Wydaje polecenia budowniczemu do stworzenia prostokatnego stolu---");
System.out.println("Budowniczy pracuje...");
budowniczy.setNazwa("Stół prostokatny rozkladany LUX");
budowniczy.setTyp("prostokatny");
budowniczy.setSzerokosc(500);
budowniczy.setDlugosc(200);
budowniczy.setWysokosc(110);
budowniczy.setMaterial("Drzewo Klonowe Lakierowane");
budowniczy.setCzyRozkladany(true);
System.out.println("---Stół prostokatny gotowy---");
}
}
4. Na koniec klasa klienta w której wytestujemy działanie programu.
class Klient {
public static void main (String[] args){
Dyrektor dyrektor = new Dyrektor();
Budowniczy budowniczyStoluOkraglego = new BudowniczyStolu();
dyrektor.stworzOkraglyStol(budowniczyStoluOkraglego);
Stol stolOkragly = budowniczyStoluOkraglego.getStol();
stolOkragly.opisStolu();
Budowniczy budowniczyStoluProstokatnego = new BudowniczyStolu();
dyrektor.stworzProstokatnyStol(budowniczyStoluProstokatnego);
Stol stolProstokatny = budowniczyStoluProstokatnego.getStol();
stolProstokatny.opisStolu();
}
}
5. Wyniki z konsoli.

ZALETY:
- Hermetyzuje operacje niezbędne do stworzenia złożonego obiektu
- Umożliwia tworzenie obiektu w procedurze wielokrokowej i nie narzuca jej z góy
- Ukrywa wewnętrzną implementacje produktu przed klientem
- Implementacje produktów mogą być wymieniane, ponieważ korzysta tylko z abstrakcyjnego interfejsu
WADY:
- Tworzenie obiektów wymaga od klienta większej wiedzy z zakresu dziedziny problemu
- Kod staje się bardziej skomplikowany, gdyż wdrożenie tego wzorca wiąże się z dodaniem wielu nowych klas.
Źródła:
- Eric Freeman, Elisabeth Freeman, Bert Bates, Kathy Sierra – „Rusz głową! Wzorce Projektowe”
- https://refactoring.guru/pl/design-patterns/builder