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.