Budowniczy jest wzorcem kreacyjnym umożliwiającym tworzenie obiektów etapami, krok po kroku.
Wzorzec budowniczy nie zakłada definiowania wspólnego interfejsu dla obiektów, w przeciwieństwie do wzorca projektowego Fabryka.
Problem:
Stwórzmy prosty program który będzie imitował fabrykę samochodową różnych marek.
Jak taki program może wyglądać bez użycia wzorca projektowego?

class Car:
package cars; public class Car { private String make; private String model; private String bodyType; private String fuelType; private String color; private String navigation; private int airbags; private int power; private int doors; public Car(String make, String model, String bodyType, String fuelType, String color, String navigation, int airbags, int power, int doors) { this.make = make; this.model = model; this.bodyType = bodyType; this.fuelType = fuelType; this.color = color; this.navigation = navigation; this.airbags = airbags; this.power = power; this.doors = doors; } public String getMake() { return make; } public void setMake(String make) { this.make = make; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public String getBodyType() { return bodyType; } public void setBodyType(String bodyType) { this.bodyType = bodyType; } public String getFuelType() { return fuelType; } public void setFuelType(String fuelType) { this.fuelType = fuelType; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getNavigation() { return navigation; } public void setNavigation(String navigation) { this.navigation = navigation; } public int getAirbags() { return airbags; } public void setAirbags(int airbags) { this.airbags = airbags; } public int getPower() { return power; } public void setPower(int power) { this.power = power; } public int getDoors() { return doors; } public void setDoors(int doors) { this.doors = doors; } }
class Main:
import cars.Car; public class Main { public static void main(String[] args) { Car bmw = new Car("BMW", "E46", "Coupe", "Diesel","Black", "Original bmw navigation", 4,180,2); Car mercedes = new Car("Mercedes", "W123", "Saloon", "PB","Blue", "Original Mercedes navigation", 2, 130, 5); } }
Myślę, że zdążyłeś już zauważyć w czym jest problem. Konstruktory są długie niczym poniedziałek, no i co w przypadku gdybyśmy z naszej fabryki chcieli wypuścić samochód bez nawigacji albo samą karoserie bez silnika? Zmuszeni jesteśmy do tworzenia wielu nowych konstruktorów z odpowiednimi parametrami. Problem pojawia się również przy tworzeniu obiektu, łatwo możemy pomylić na przykład ilość poduszek powietrznych z ilością koni mechanicznych, wyobrażasz sobie samochód z dziewięćdziesięcioma poduszkami powietrznymi i z zaledwie czterema końmi mechanicznymi? No właśnie, dlatego z pomocą przychodzi nam wzorzec Budowniczy.
Rozwiązanie (Budowniczy z klasą wewnętrzną):
class Car/CarBuilder:
package cars; public class Car { private String make; private String model; private String bodyType; private String fuelType; private String color; private String navigation; private int airbags; private int power; private int doors; private Car(CarBuilder carBuilder) { this.make = carBuilder.make; this.model = carBuilder.model; this.bodyType = carBuilder.bodyType; this.fuelType = carBuilder.fuelType; this.color = carBuilder.color; this.navigation = carBuilder.navigation; this.airbags = carBuilder.airbags; this.power = carBuilder.power; this.doors = carBuilder.doors; } public String getMake() { return make; } public String getModel() { return model; } public String getBodyType() { return bodyType; } public String getFuelType() { return fuelType; } public String getColor() { return color; } public String getNavigation() { return navigation; } public int getAirbags() { return airbags; } public int getPower() { return power; } public int getDoors() { return doors; } @Override public String toString() { return "Car{" + "make='" + make + '\'' + ", model='" + model + '\'' + ", bodyType='" + bodyType + '\'' + ", fuelType='" + fuelType + '\'' + ", color='" + color + '\'' + ", navigation='" + navigation + '\'' + ", airbags=" + airbags + ", power=" + power + ", doors=" + doors + '}'; } public static class CarBuilder { private String make; private String model; private String bodyType; private String fuelType; private String color; private String navigation; private int airbags; private int power; private int doors; public CarBuilder setMake(String make){ this.make = make; return this; } public CarBuilder setModel(String model){ this.model = model; return this; } public CarBuilder setBodyType(String bodyType){ this.bodyType = bodyType; return this; } public CarBuilder setFuelType(String fuelType){ this.fuelType = fuelType; return this; } public CarBuilder setColor(String color){ this.color = color; return this; } public CarBuilder setNavigation(String navigation){ this.navigation = navigation; return this; } public CarBuilder setAirbags(int airbags){ this.airbags = airbags; return this; } public CarBuilder setPower(int power){ this.power = power; return this; } public CarBuilder setDoors(int doors){ this.doors = doors; return this; } public Car build(){ return new Car(this); } } }
class Main:
public class Main { public static void main(String[] args) { Car bmw = new Car.CarBuilder() .setMake("BMW") .setModel("X6") .setColor("Black") .setNavigation("Sony") .setFuelType("PB") .setAirbags(8) .setBodyType("SUV") .setDoors(5) .setPower(500) .build(); Car polonez = new Car.CarBuilder() .setMake("FSO") .setModel("Caro") .setFuelType("PB") .setColor("Red") .setPower(50) .setDoors(5) .setBodyType("Saloon") .build(); Car mercedesPrototype = new Car.CarBuilder() .setMake("Mercedes") .setModel("W800") .setColor("Blue") .setDoors(2) .setBodyType("Coupe") .build(); } }
Prawda że od razu widać różnicę? Tworzenie nowych obiektów staje się bardziej przejrzyste. Wystarczy że odpowiednio zaimplementujemy wzorzec budowniczy i problemy z którymi mogliśmy się wcześniej borykać idą w zapomnienie. Co dokładnie zrobiliśmy?
Do klasy „Car” zaimplementowaliśmy klasę wewnętrzną „CarBuilder” z takimi samymi polami. Dodaliśmy do niej metody ustawiające odpowiednie parametry produkowanego samochodu, które zwracały obiekt „CarBuilder” oraz jedną metodę „build” która zwraca nam już gotowy obiekt typu „Car”. W klasie „Car” ustawiliśmy konstruktor jako prywatny, żeby nikt z zewnątrz nie miał do niego dostępu. W samym konstruktorze pola przypisujemy do pól z klasy „CarBuilder”. Jak widać utworzenie takiego pojazdu jest znacznie czytelniejsze, wystarczy zaimplementować obiekt typu „Carbuilder()” a następnie po kropce dodawać parametry które nas interesują oraz nadać im wartości.
Mam nadzieję, że chociaż trochę pomogłem zrozumieć Ci wzorzec Budowniczy.