Wzorzec projektowy Builder jest wzorcem konstrukcyjnym. Jego celem jest hermetyzacja kodu odpowiedzialnego za tworzenie obiektów od kodu klienta. Od wzorców z rodziny fabryk różni go jeszcze to, że Builder potrafi budować obiekty wg różnych algorytmów krok po kroku – Fabryki budują obiekty o zbliżonych funkcjonalnościach w jednym etapie.
Wzorzec Builder składa się: z interfejsu reprezentującego abstrakcyjnego budowniczego, klas go implementujących jako konkretni budowniczowie, Klasy Director, która zarządza procesem budowania, oraz produktem który ma zostać wyprodukowany.
Produkt który chcemy produkować:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Drink { private String name = ""; private Map<String, Integer> plyny = new HashMap<String, Integer>(); //nazwa i obj. w ml private List<String> dodatki = new ArrayList<String>(); public void setName(String name) { this.name = name; } public void addPlyn(String nazwa, int ilosc) { plyny.put(nazwa, ilosc); } public void addDodatek(String nazwa) { dodatki.add(nazwa); } } |
Tworzymy abstrakcyjnego Buildera:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public abstract class DrinkBuilder { protected Drink drink; public Drink getDrink() { return drink; } public void createNewDrink() { drink = new Drink(); } public abstract void ustalNazwe(); public abstract void wlejPlyny(); public abstract void dodajDodatki(); } |
oraz dwóch konkretnych budowniczych:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class HellRazorDrinkBuilder extends DrinkBuilder { public void ustalNazwe() { drink.setName("Hell Razor"); } public void wlejPlyny() { drink.addPlyn("Whiskey", 33); drink.addPlyn("Tequila", 33); drink.addPlyn("Wódka", 33); } public void dodajDodatki() { } } public class ArizonaSunriseDrinkBuilder extends DrinkBuilder { public void ustalNazwe() { drink.setName("Arizona Sunrise"); } public void wlejPlyny() { drink.addPlyn("Tequila", 45); drink.addPlyn("Sok cytrynowy", 15); drink.addPlyn("Sok pomarańczowy", 120); } public void dodajDodatki() { drink.addDodatek("Grenadina"); } } |
Budowniczowie budują drinki wg konkrentego przepisu.
Teraz czas na Directora u nas nazwanego Barmanem, który zarządza procesem tworzenia drinków:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Barman { private DrinkBuilder drinkBuilder; public void setDrinkBuilder(DrinkBuilder db) { this.drinkBuilder = db; } public Drink getDrink() { return drinkBuilder.getDrink(); } public void zrobDrinka() { drinkBuilder.createNewDrink(); drinkBuilder.ustalNazwe(); drinkBuilder.wlejPlyny(); drinkBuilder.dodajDodatki(); } } |
Rola Barmana jest tutaj bardzo ważna, w tej klasie możemy umieścić kod, który zarządza co ma być zbudowane. W naszym przypadku Barman buduje to co mu podamy, ale nie stoi nic na przykładzie dodanie np takiego kodu (abstrachując):
1 2 3 4 5 6 |
if(zamawiajacy == ladnaDziewczyna){ drinkBuilder.dodajExtraDodatki(); //albo drinkBuilder.podajWCzystejSzklance(); } |
czyli wywołanie niektórych metod buildera uzależnić od instrukcji warunkowych.
A tak nasz Budowniczy może być wykorzystywany:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class SmakoszDrinkow { public static void main(String[] args) { Barman barman = new Barman(); DrinkBuilder hellRazor = new HellRazorDrinkBuilder(); barman.setDrinkBuilder(hellRazor); barman.zrobDrinka(); Drink drink = barman.getDrink(); } } |
Diagram wzorca: