Middle
Czym jest builder (budowniczy) w kontekście rozwoju oprogramowania?
sobes.tech AI
Odpowiedź od AI
Wzorzec projektowy, który oddziela konstruowanie złożonego obiektu od jego reprezentacji, umożliwiając tworzenie różnych reprezentacji tego samego procesu budowy.
Kluczowe cechy:
- Oddzielenie odpowiedzialności: Logika tworzenia obiektu (etapy konstruowania) i reprezentacja obiektu (produkt końcowy) są oddzielone.
- Budowa krok po kroku: Obiekt jest budowany krok po kroku, co czyni proces bardziej elastycznym i zrozumiałym.
- Różne reprezentacje: Ten sam proces konstruowania może być używany do tworzenia różnych wariantów obiektu.
- Enkapsulacja: Szczegóły tworzenia obiektu są ukryte przed klientem, co upraszcza jego użycie.
Główni uczestnicy:
- Builder (Budowniczy): Abstrakcyjny interfejs lub klasa definiująca kroki do tworzenia części produktu.
- ConcreteBuilder (Konkretna Budowniczy): Konkretna implementacja Budowniczego, wykonująca faktyczne konstruowanie części produktu i przechowująca wynik.
- Director (Reżyser): Klasa, która używa obiektu ConcreteBuilder do konstruowania produktu, wywołując odpowiednie metody w określonej kolejności.
- Product (Produkt): Złożony obiekt, który jest konstruowany.
Przykład użycia:
Tworzenie obiektu Pizza z różnymi składnikami:
// Produkt
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 addTopping(String topping) {
if (toppings == null) {
toppings = new ArrayList<>();
}
toppings.add(topping);
}
@Override
public String toString() {
return "Pizza z ciastem: " + dough + ", sos: " + sauce + ", ser: " + cheese + ", dodatki: " + toppings;
}
}
// Interfejs budowniczego
interface PizzaBuilder {
void buildDough();
void buildSauce();
void buildCheese();
void addTopping(String topping);
Pizza getResult();
}
// Konkretna budowniczy
class MargheritaPizzaBuilder implements PizzaBuilder {
private Pizza pizza;
public MargheritaPizzaBuilder() {
this.pizza = new Pizza();
}
@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 ma dodatkowych składników domyślnie
}
@Override
public Pizza getResult() {
return pizza;
}
}
// Reżyser
class Cook {
private PizzaBuilder pizzaBuilder;
public void setPizzaBuilder(PizzaBuilder pb) {
pizzaBuilder = pb;
}
public void constructPizza() {
pizzaBuilder.buildDough();
pizzaBuilder.buildSauce();
pizzaBuilder.buildCheese();
// Dodatkowe składniki mogą być dodane tutaj w zależności od typu pizzy lub wymagań
}
}
// Kod klienta
public class PizzaShop {
public static void main(String[] args) {
Cook cook = new Cook();
PizzaBuilder margheritaBuilder = new MargheritaPizzaBuilder();
cook.setPizzaBuilder(margheritaBuilder);
cook.constructPizza();
Pizza pizza = margheritaBuilder.getResult();
System.out.println(pizza);
}
}
Zastosowanie gdy:
- Konieczne jest tworzenie złożonego obiektu z wieloma opcjonalnymi parametrami.
- Proces tworzenia obiektu jest sekwencyjny i obejmuje wiele kroków.
- Należy tworzyć różne warianty tego samego obiektu, używając tego samego algorytmu konstruowania.
- Chcemy uniknąć konstruktora z dużą liczbą parametrów ("telescoping constructor").
Zalety:
- Poprawia czytelność i utrzymanie kodu.
- Zapewnia elastyczność w tworzeniu obiektów.
- Umożliwia tworzenie obiektów niemutowwalnych.
- Eliminuję potrzebę wielu konstruktorów.
Wady:
- Może zwiększyć liczbę klas w projekcie (dla każdego typu obiektu i każdego budowniczego).
- Wymaga początkowego wysiłku projektowego.