Middle
Czym jest wzorzec projektowy 'budowniczy' (Builder)?
sobes.tech AI
Odpowiedź od AI
Wzorzec "Budowniczy" (Builder) jest wzorcem kreacyjnym przeznaczonym do tworzenia złożonych obiektów krok po kroku. Oddziela proces konstruowania złożonego obiektu od jego reprezentacji, tak aby ten sam proces konstruowania mógł tworzyć różne reprezentacje.
Zasady działania:
- Oddzielenie: Oddziela logikę tworzenia obiektu od samego obiektu.
- Delegacja: Kod klienta (Director) deleguje tworzenie obiektu do osobnej klasy (Builder).
- Krokowa budowa: Obiekt jest tworzony z komponentów za pomocą zestawu metod buildera.
Główni uczestnicy:
- Produkt: Złożony obiekt, który chcemy zbudować. Nie zależy od procesu tworzenia.
- Builder: Abstrytny interfejs definiujący kroki do budowy Produktu.
- ConcreteBuilder: Konkretna implementacja Buildera, realizująca kroki budowy i udostępniająca metodę do uzyskania gotowego Produktu.
- Director (Opcjonalnie): Określa kolejność wywołań kroków budowy. Nie jest obowiązkowy, kod klienta może bezpośrednio współpracować z ConcreteBuilder.
Zalety:
- Umożliwia tworzenie obiektów z dużą liczbą parametrów opcjonalnych w czytelniejszy sposób niż za pomocą konstruktorów z wieloma argumentami lub łańcuchów setterów.
- Umożliwia tworzenie różnych wariantów tego samego obiektu, korzystając z tego samego procesu budowy.
- Ukrywa wewnętrzną strukturę tworzonego obiektu.
Wady:
- Może zwiększyć liczbę klas w projekcie (Builder, ConcreteBuilder, Product, Director).
Przykład użycia: Tworzenie obiektu klasy Pizza z różnymi składnikami.
// Produkt
public class Pizza {
private String dough;
private String sauce;
private String cheese;
private List<String> toppings;
public void setDough(String dough) { this.dough = dough; }
public void setSauce(String sauce) { this.sauce = sauce; }
public void setCheese(String cheese) { this.cheese = cheese; }
public void setToppings(List<String> toppings) { this.toppings = toppings; }
@Override
public String toString() {
return "Pizza z ciasta=" + dough + ", sos=" + sauce + ", ser=" + cheese + ", dodatki=" + toppings;
}
}
// Builder
public interface PizzaBuilder {
void buildDough();
void buildSauce();
void buildCheese();
void addTopping(String topping);
Pizza getPizza();
}
// ConcreteBuilder
public class MargheritaPizzaBuilder implements PizzaBuilder {
private Pizza pizza;
public MargheritaPizzaBuilder() {
this.pizza = new Pizza();
this.pizza.setToppings(new ArrayList<>()); // Inicjalizacja listy
}
@Override
public void buildDough() {
pizza.setDough("Cienkie ciasto");
}
@Override
public void buildSauce() {
pizza.setSauce("Sos pomidorowy");
}
@Override
public void buildCheese() {
pizza.setCheese("Mozzarella");
}
@Override
public void addTopping(String topping) {
// Margherita nie dodaje toppingów oprócz podstawowych
}
@Override
public Pizza getPizza() {
return this.pizza;
}
}
// Director (Opcjonalnie)
public class Kitchen {
private PizzaBuilder pizzaBuilder;
public void setPizzaBuilder(PizzaBuilder pb) {
pizzaBuilder = pb;
}
public Pizza getPizza() {
return pizzaBuilder.getPizza();
}
public void constructPizza() {
pizzaBuilder.buildDough();
pizzaBuilder.buildSauce();
pizzaBuilder.buildCheese();
// Mogą być wywoływane inne metody, np. addTopping() w innym builderze
}
}
Użycie:
// Kod klienta
Kitchen kitchen = new Kitchen();
PizzaBuilder margheritaBuilder = new MargheritaPizzaBuilder();
kitchen.setPizzaBuilder(margheritaBuilder);
kitchen.constructPizza();
Pizza margherita = kitchen.getPizza();
System.out.println(margherita); // Wypisze: Pizza z cienkim ciastem, sosem pomidorowym, mozzarellą, bez dodatków