Wzorzec Builder
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 67 z 87.
Wzorzec Builder to kreacyjny wzorzec projektowy, który rozwiązuje częsty problem: tworzenie złożonych obiektów z wieloma opcjonalnymi parametrami. Zamiast tworzyć wiele konstruktorów lub przekazywać null dla nieużywanych parametrów, możesz za pomocą Buildera konstruować obiekty krok po kroku.
Rozważ utworzenie obiektu User z wymaganymi polami (imię, e-mail) i opcjonalnymi (wiek, telefon, adres). Bez wzorca Buildera potrzebne byłyby konstruktory teleskopowe lub nieporządny konstruktor z wieloma parametrami. Wzorzec Builder oferuje czystsze podejście:
public class User {
private final String name; // wymagane
private final String email; // wymagane
private final int age; // opcjonalne
private final String phone; // opcjonalne
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
}
public static class UserBuilder {
private final String name;
private final String email;
private int age;
private String phone;
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
}Metody Buildera zwracają this, umożliwiając łączenie metod: płynny i czytelny sposób konstruowania obiektów:
User user = new User.UserBuilder("Alice", "alice@email.com")
.age(25)
.phone("555-1234")
.build();Zwróć uwagę, że konstruktor User jest prywatny: obiekty można tworzyć tylko za pomocą Buildera. Ten wzorzec sprawdza się doskonale, gdy obiekty mają wiele opcjonalnych pól, dzięki czemu kod jest bardziej czytelny i mniej podatny na błędy niż w przypadku długich list parametrów.
Wyzwanie
ŁatwyZbudujmy system zamawiania pizzy z użyciem wzorca Builder! Zamiast korzystać z konstruktora przyjmującego dziesiątki parametrów dla wszystkich możliwych dodatków i opcji, utworzysz płynny builder, dzięki któremu dostosowywanie pizzy będzie przejrzyste i czytelne.
Rozmieścisz swój kod w dwóch plikach:
Pizza.java: Utwórz klasęPizza, która wykorzystuje wzorzec Builder do tworzenia obiektów. Twoja pizza powinna mieć następujące pola:size(String) — wymagane, określa rozmiar pizzycheese(boolean) — opcjonalne, domyślnie ma wartość falsepepperoni(boolean) — opcjonalne, domyślnie ma wartość falsemushrooms(boolean) — opcjonalne, domyślnie ma wartość falseKonstruktor
Pizzapowinien być prywatny i przyjmować jako parametr wyłączniePizzaBuilder, kopiując wszystkie wartości z buildera do pól pizzy.Dodaj metodę
getDescription(), która zwraca ciąg znaków w tym formacie:[size] Pizza with: [toppings], gdzie toppings to oddzielona przecinkami lista dodatków, których wartość wynosi true (Cheese, Pepperoni, Mushrooms). Jeśli nie wybrano żadnych dodatków, część toppings powinna zawierać tylkono toppings.Utwórz statyczną klasę wewnętrzną
PizzaBuilder, zawierającą:- Konstruktor przyjmujący wymagany parametr
size - Metody
cheese(),pepperoni()imushrooms(), z których każda ustawia odpowiednią wartość logiczną na true i zwracathis, aby umożliwić łańcuchowe wywołania - Metodę
build(), która zwraca nowy obiektPizza
- Konstruktor przyjmujący wymagany parametr
Main.java: Pokaż działanie buildera pizzy! Otrzymasz cztery dane wejściowe: rozmiar pizzy (String) oraz trzy wartości tak/nie (String) określające, czy dodać odpowiednio ser, pepperoni i pieczarki.Utwórz
PizzaBuilderz podanym rozmiarem. Następnie na podstawie każdej wartości tak/nie wywołaj odpowiednią metodę dodatku w łańcuchu. Jeśli wartość wejściowa to"yes", wywołaj metodę danego dodatku; jeśli"no", pomiń ją. Na koniec wywołajbuild(), aby utworzyć pizzę.Wyświetl wynik wywołania
getDescription()na gotowej pizzy.
Otrzymasz cztery dane wejściowe w następującej kolejności: size (String), add cheese (String: "yes" lub "no"), add pepperoni (String: "yes" lub "no"), add mushrooms (String: "yes" lub "no").
Na przykład dla danych wejściowych Large, yes, no, yes wynikiem będzie: Large Pizza with: Cheese, Mushrooms
Zwróć uwagę, że wzorzec Builder pozwala tworzyć pizze z dowolną kombinacją dodatków bez konieczności tworzenia osobnych konstruktorów dla każdej możliwości!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Odczytaj dane wejściowe
String size = scanner.nextLine();
String addCheese = scanner.nextLine();
String addPepperoni = scanner.nextLine();
String addMushrooms = scanner.nextLine();
// TODO: Utwórz PizzaBuilder z podanym rozmiarem
// TODO: Połącz wywołania metod dodających składniki w łańcuch na podstawie odpowiedzi tak/nie
// TODO: Zbuduj pizzę i wypisz jej opis
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatnościPoćwicz samodzielnie: Kompilator Java online