Mateusz Wajnberger
Budowniczy – kreacyjny wzorzec projektowy
Rozpatrzymy dwie odmiany danego wzorca projektowego: Budowniczy z Klasą Wewnętrzną (ang. Builder with Inner Class) oraz Klasyczny Budowniczy (ang. Classic Builder). Jako przykład weźmiemy projekt budowy Hotelu. Dla przejrzystości przytoczonego przykładu przyjmiemy niewielką ilość pól oraz konstruktorów.
Kod przed implementacją
Tworzymy klasę z kilkoma polami, dodajemy settery i gettery dla każdego pola oraz dwa różne konstruktory (jeden z wszystkimy polami, natomiast drugi będzie pozbawiony basenu).
public class Hotel {
private String reception;
private String apartments;
private String swimmingPool;
public Hotel(String reception, String apartments, String swimmingPool) {
this.reception = reception;
this.apartments = apartments;
this.swimmingPool = swimmingPool;
}
public Hotel(String reception, String apartments) {
this.reception = reception;
this.apartments = apartments;
}
public String getReception() {
return reception;
}
public void setReception(String reception) {
this.reception = reception;
}
public String getApartments() {
return apartments;
}
public void setApartments(String apartments) {
this.apartments = apartments;
}
public String getSwimmingPool() {
return swimmingPool;
}
public void setSwimmingPool(String swimmingPool) {
this.swimmingPool = swimmingPool;
}
@Override
public String toString() {
return "Hotel{" +
"reception='" + reception + '\'' +
", apartments='" + apartments + '\'' +
", swimmingPool='" + swimmingPool + '\'' +
'}';
}
}
Przechodzimy do tworzenia konkretnych hoteli za pomocą konstruktorów co w przypadku ich dużej ilości przy różnych projektach hoteli może być sprawą problematyczną, gdyż możemy nie być pewni, które pola powinny zostać zainicjalizowane.
public class Main {
public static void main(String[] args) {
Hotel firstHotel = new Hotel("reception", "apartments", "swimmingPool");
Hotel secondHotel = new Hotel("reception", "apartments");
}
}
Implementacja – Budowniczy z Klasą Wewnętrzną
Implementujemy klasę statyczną, która posiada te same pola co nasza główna klasa oraz dodajemy metodę, która będzie zwracała stworzony hotel. Jeśli ktoś chciałby stworzyć instancje naszego hotelu, to skorzysta z naszej klasy wewnętrznej, a co za tym idzie z dowolnej ilości metod.
public class Hotel {
private String reception;
private String apartments;
private String swimmingPool;
private Hotel(HotelBuilder hotelBuilder) {
this.reception = hotelBuilder.reception;
this.apartments = hotelBuilder.apartments;
this.swimmingPool = hotelBuilder.swimmingPool;
}
public String getReception() {
return reception;
}
public String getApartments() {
return apartments;
}
public String getSwimmingPool() {
return swimmingPool;
}
@Override
public String toString() {
return "Hotel{" +
"reception='" + reception + '\'' +
", apartments='" + apartments + '\'' +
", swimmingPool='" + swimmingPool + '\'' +
'}';
}
public static class HotelBuilder {
private String reception;
private String apartments;
private String swimmingPool;
public HotelBuilder buildReception(String reception) {
this.reception = reception;
return this;
}
public HotelBuilder buildApartments(String apartments) {
this.apartments = apartments;
return this;
}
public HotelBuilder buildSwimmingPool(String swimmingPool) {
this.swimmingPool = swimmingPool;
return this;
}
public Hotel build() {
return new Hotel(this);
}
}
}
Tworzymy gotowy obiekt wybierając poszczególne metody według uznania.
public class Main {
public static void main(String[] args) {
Hotel hotel = new Hotel.HotelBuilder()
.buildReception("reception")
.buildApartments("apartments")
.buildSwimmingPool("swimmingPool")
.build();
System.out.println(hotel);
}
}
Implementacja – Klasyczny Budowniczy
Implementację zaczynamy od uporządkowania klasy „Hotel”. W tym celu usuwamy konstruktory, gdyż nie będą już nam potrzebne.
public class Hotel {
private String reception;
private String apartments;
private String swimmingPool;
public String getReception() {
return reception;
}
public void setReception(String reception) {
this.reception = reception;
}
public String getApartments() {
return apartments;
}
public void setApartments(String apartments) {
this.apartments = apartments;
}
public String getSwimmingPool() {
return swimmingPool;
}
public void setSwimmingPool(String swimmingPool) {
this.swimmingPool = swimmingPool;
}
@Override
public String toString() {
return "Hotel{" +
"reception='" + reception + '\'' +
", apartments='" + apartments + '\'' +
", swimmingPool='" + swimmingPool + '\'' +
'}';
}
}
Tworzymy interfejs z poszczególnymi metodami, które po późniejszej implementacji posłużą do budowy poszczególnych elementów naszego hotelu.
public interface HotelBuilder {
void buildReception();
void buildApartments();
void buildSwimmingPool();
Hotel getHotel();
}
Kolejny etap to utworzenie tak zwanego Dyrektora, czyli klasy, która definiuje kolejność w jakiej należy wywołać etapy konstrukcyjne. Zawiera ona konstruktor, w którym przekazywane są obiekty typu „HotelBuilder”.
public class HotelDirector {
private HotelBuilder hotelBuilder;
public HotelDirector(HotelBuilder hotelBuilder) {
this.hotelBuilder = hotelBuilder;
}
public void buildHotel() {
hotelBuilder.buildReception();
hotelBuilder.buildApartments();
hotelBuilder.buildSwimmingPool();
}
public Hotel getHotel() {
return this.hotelBuilder.getHotel();
}
}
Następnie dodajemy już konkretny „builder”, który implementuje metody z naszego interfejsu.
public class ParticularHotelBuilder implements HotelBuilder{
private Hotel hotel;
public ParticularHotelBuilder() {
this.hotel = new Hotel();
}
@Override
public void buildReception() {
this.hotel.setReception("Particular reception");
}
@Override
public void buildApartments() {
this.hotel.setApartments("Particular apartments");
}
@Override
public void buildSwimmingPool() {
this.hotel.setSwimmingPool("Particular swimming pool");
}
@Override
public Hotel getHotel() {
return this.hotel;
}
}
Ostatni etap to utworzenie obiektu typu „Hotel” pod kierownictwem naszego Dyrektora.
public class Main {
public static void main(String[] args) {
ParticularHotelBuilder particularHotelBuilder = new ParticularHotelBuilder();
HotelDirector hotelDirector = new HotelDirector(particularHotelBuilder);
hotelDirector.buildHotel();
Hotel particularHotel = hotelDirector.getHotel();
System.out.println(particularHotel);
}
}