Przeznaczenie
Builder to kreacyjny wzorzec projektowy, którego celem jest oddzielenie procesu tworzenia obiektu od jego reprezentacji. Umożliwia budowanie złożonych obiektów krok po kroku, a także eliminuje potrzebę stosowania wielu przeciążonych konstruktorów. Sprawdza się szczególnie dobrze w przypadku klas posiadających wiele opcjonalnych pól, ponieważ umożliwia tworzenie obiektów w sposób bardziej przejrzysty i elastyczny.
Dlaczego go potrzebujemy ?
W sytuacjach, gdy:
- mamy wiele pól (niektóre opcjonalne),
- klasa posiada wiele konstruktorów, co utrudnia ich rozróżnienie i użycie,
- kolejność parametrów jest łatwa do pomylenia,
- chcemy zwiększyć czytelność i bezpieczeństwo kodu przy tworzeniu obiektów —
wzorzec Builder pozwala nam zminimalizować ryzyko błędów i zwiększyć czytelność kodu. Ułatwia również dodawanie nowych pól w przyszłości, bez potrzeby modyfikowania wielu konstruktorów.
Poniżej przykładowy kod przed użyciem wzorca Builder:
public class Car {
private String brand;
private String model;
private int year;
private String color;
private String fuelType;
private double engineSize;
private int horsepower;
private String transmission;
private int mileage;
private boolean isElectric;
public Car(String brand, String model, int year, String color, String fuelType, double engineSize, int horsepower, String transmission, int mileage, boolean isElectric) {
this.brand = brand;
this.model = model;
this.year = year;
this.color = color;
this.fuelType = fuelType;
this.engineSize = engineSize;
this.horsepower = horsepower;
this.transmission = transmission;
this.mileage = mileage;
this.isElectric = isElectric;
}
public Car(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
public Car(String brand, String model, int year, String color, String fuelType, double engineSize, int horsepower, String transmission, int mileage) {
this.brand = brand;
this.model = model;
this.year = year;
this.color = color;
this.fuelType = fuelType;
this.engineSize = engineSize;
this.horsepower = horsepower;
this.transmission = transmission;
this.mileage = mileage;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getFuelType() {
return fuelType;
}
public void setFuelType(String fuelType) {
this.fuelType = fuelType;
}
public double getEngineSize() {
return engineSize;
}
public void setEngineSize(double engineSize) {
this.engineSize = engineSize;
}
public int getHorsepower() {
return horsepower;
}
public void setHorsepower(int horsepower) {
this.horsepower = horsepower;
}
public String getTransmission() {
return transmission;
}
public void setTransmission(String transmission) {
this.transmission = transmission;
}
public int getMileage() {
return mileage;
}
public void setMileage(int mileage) {
this.mileage = mileage;
}
public boolean isElectric() {
return isElectric;
}
public void setElectric(boolean electric) {
isElectric = electric;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", model='" + model + '\''+
", year=" + year +
", color='" + color + '\'' +
", fuelType='" + fuelType + '\'' +
", engineSize=" + engineSize +
", horsepower=" + horsepower +
", transmission='" + transmission + '\'' +
", mileage=" + mileage +
", isElectric=" + isElectric +
'}';
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car("Fiat", "Panda" ,2009, "red", "gas", 1.1, 65, "manual", 85000, false);
System.out.println(car);
}
}
Builder z klasą wewnętrzną – schemat działania
Struktura:

Kod:
public class Car {
private String brand;
private String model;
private int year;
private String color;
private String fuelType;
private double engineSize;
private int horsepower;
private String transmission;
private int mileage;
private boolean isElectric;
private Car(CarBuilder carBuilder) {
this.brand = carBuilder.brand;
this.model = carBuilder.model;
this.year = carBuilder.year;
this.color = carBuilder.color;
this.fuelType = carBuilder.fuelType;
this.engineSize = carBuilder.engineSize;
this.horsepower = carBuilder.horsepower;
this.transmission = carBuilder.transmission;
this.mileage = carBuilder.mileage;
this.isElectric = carBuilder.isElectric;
}
public String getBrand() {
return brand;
}
public String getModel() {
return model;
}
public int getYear() {
return year;
}
public String getFuelType() {
return fuelType;
}
public String getColor() {
return color;
}
public double getEngineSize() {
return engineSize;
}
public int getHorsepower() {
return horsepower;
}
public String getTransmission() {
return transmission;
}
public int getMileage() {
return mileage;
}
public boolean isElectric() {
return isElectric;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", model='" + model + '\'' +
", year=" + year +
", color='" + color + '\'' +
", fuelType='" + fuelType + '\'' +
", engineSize=" + engineSize +
", horsepower=" + horsepower +
", transmission='" + transmission + '\'' +
", mileage=" + mileage +
", isElectric=" + isElectric +
'}';
}
public static class CarBuilder {
private String brand;
private String model;
private int year;
private String color;
private String fuelType;
private double engineSize;
private int horsepower;
private String transmission;
private int mileage;
private boolean isElectric;
public CarBuilder buildBrand(String brand) {
this.brand = brand;
return this;
}
public CarBuilder buildModel(String model) {
this.model = model;
return this;
}
public CarBuilder buildYear(int year) {
this.year = year;
return this;
}
public CarBuilder buildColor(String color) {
this.color = color;
return this;
}
public CarBuilder buildFuelType(String fuelType) {
this.fuelType = fuelType;
return this;
}
public CarBuilder buildEngineSize(double engineSize) {
this.engineSize = engineSize;
return this;
}
public CarBuilder buildHorsePower(int horsepower) {
this.horsepower = horsepower;
return this;
}
public CarBuilder buildTransmission(String transmission) {
this.transmission = transmission;
return this;
}
public CarBuilder buildMileage(int mileage) {
this.mileage = mileage;
return this;
}
public CarBuilder buildIsElectric(boolean isElectric) {
this.isElectric = isElectric;
return this;
}
public Car build() {
return new Car(this);
}
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car.CarBuilder()
.buildBrand("Fiat")
.buildModel("Panda")
.buildYear(2009)
.buildColor("red")
.buildFuelType("gas")
.buildEngineSize(1.1)
.buildHorsePower(60)
.buildTransmission("manual")
.buildMileage(85000)
.buildIsElectric(false)
.build();
System.out.println(car);
}
}
Builder wersja klasyczna – schemat działania
Struktura:

Kod:
public interface CarBuilder {
void buildBrand();
void buildModel();
void buildYear();
void buildColor();
void buildFuelType();
void buildEngineSize();
void buildHorsePower();
void buildTransmission();
void buildMileage();
void buildIsElectric();
Car getCar();
}
public class CarDirector {
private CarBuilder carBuilder;
public CarDirector(CarBuilder carBuilder) {
this.carBuilder = carBuilder;
}
public void buildCar() {
carBuilder.buildBrand();
carBuilder.buildModel();
carBuilder.buildYear();
carBuilder.buildColor();
carBuilder.buildFuelType();
carBuilder.buildEngineSize();
carBuilder.buildHorsePower();
carBuilder.buildTransmission();
carBuilder.buildMileage();
carBuilder.buildIsElectric();
}
public Car getCar() {return this.carBuilder.getCar();}
}
public class Car {
private String brand;
private String model;
private int year;
private String color;
private String fuelType;
private double engineSize;
private int horsepower;
private String transmission;
private int mileage;
private boolean isElectric;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
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 int getHorsepower() {
return horsepower;
}
public void setHorsepower(int horsepower) {
this.horsepower = horsepower;
}
public double getEngineSize() {
return engineSize;
}
public void setEngineSize(double engineSize) {
this.engineSize = engineSize;
}
public String getTransmission() {
return transmission;
}
public void setTransmission(String transmission) {
this.transmission = transmission;
}
public int getMileage() {
return mileage;
}
public void setMileage(int mileage) {
this.mileage = mileage;
}
public boolean isElectric() {
return isElectric;
}
public void setElectric(boolean electric) {
isElectric = electric;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", model='" + model + '\'' +
", year=" + year +
", color='" + color + '\'' +
", fuelType='" + fuelType + '\'' +
", engineSize=" + engineSize +
", horsepower=" + horsepower +
", transmission='" + transmission + '\'' +
", mileage=" + mileage +
", isElectric=" + isElectric +
'}';
}
}
public class PassengerCarBuilder implements CarBuilder {
private Car car;
public PassengerCarBuilder() {
this.car = new Car();
}
@Override
public void buildBrand() {
this.car.setBrand("Fiat");
}
@Override
public void buildModel() {
this.car.setModel("Panda");
}
@Override
public void buildYear() {
this.car.setYear(2009);
}
@Override
public void buildColor() {
this.car.setColor("red");
}
@Override
public void buildFuelType() {
this.car.setFuelType("gas");
}
@Override
public void buildEngineSize() {
this.car.setEngineSize(1.1);
}
@Override
public void buildHorsePower() {
this.car.setHorsepower(60);
}
@Override
public void buildTransmission() {
this.car.setTransmission("manual");
}
@Override
public void buildMileage() {
this.car.setMileage(85000);
}
@Override
public void buildIsElectric() {
this.car.setElectric(false);
}
@Override
public Car getCar() {
return car;
}
}
public class VanCarBuilder implements CarBuilder {
private Car car;
public VanCarBuilder() {
this.car = new Car();
}
@Override
public void buildBrand() {
this.car.setBrand("Fiat");
}
@Override
public void buildModel() {
this.car.setModel("Panda");
}
@Override
public void buildYear() {
this.car.setYear(2009);
}
@Override
public void buildColor() {
this.car.setColor("red");
}
@Override
public void buildFuelType() {
this.car.setFuelType("gas");
}
@Override
public void buildEngineSize() {
this.car.setEngineSize(1.1);
}
@Override
public void buildHorsePower() {
this.car.setHorsepower(60);
}
@Override
public void buildTransmission() {
this.car.setTransmission("manual");
}
@Override
public void buildMileage() {
this.car.setMileage(85000);
}
@Override
public void buildIsElectric() {
this.car.setElectric(false);
}
@Override
public Car getCar() {
return car;
}
}
public class Main {
public static void main(String[] args) {
PassengerCarBuilder passengerCarBuilder = new PassengerCarBuilder();
VanCarBuilder vanCarBuilder = new VanCarBuilder();
CarDirector passengerCarDirector = new CarDirector(passengerCarBuilder);
passengerCarDirector.buildCar();
CarDirector varCarDirector = new CarDirector(vanCarBuilder);
varCarDirector.buildCar();
Car passengerCar = passengerCarDirector.getCar();
Car vanCar = vanCarBuilder.getCar();
System.out.println(passengerCar);
System.out.println(vanCar);
}
}
Podsumowanie
Wzorzec Builder stosujemy przede wszystkim wtedy, gdy klasa ma wiele pól, a tworzenie wielu konstruktorów byłoby niepraktyczne i nieczytelne. Builder pozwala na wygodne i bezpieczne konstruowanie obiektów krok po kroku, bez konieczności udostępniania setterów, dzięki czemu po utworzeniu obiektu jego stan pozostaje niezmienny.
Wariant z klasą wewnętrzną umożliwia użytkownikowi elastyczne i czytelne tworzenie obiektu, umożliwiając ustawienie tylko tych pól, które są potrzebne. Po zbudowaniu obiektu dalsza modyfikacja jest niemożliwa.
Wersja klasyczna Buildera, wykorzystująca dyrektora i osobne klasy budownicze, sprawdza się, gdy chcemy precyzyjnie kontrolować proces tworzenia obiektu i oddzielić go od użytkownika — dyrektor decyduje, w jaki sposób obiekt zostanie zbudowany, a użytkownik nie musi się tym zajmować.
Builder to popularny wzorzec projektowy, który znacząco poprawia czytelność, elastyczność i bezpieczeństwo kodu przy tworzeniu złożonych obiektów. W praktyce często spotyka się jego różne wariacje, ale charakterystyczne cechy pozostają niezmienne — stopniowe budowanie obiektu i oddzielenie konstrukcji od reprezentacji.
