Wzorce projektowe w Android – Builder

Builder

Wzorzec Builder upraszcza tworzenie obiektów w bardzo czysty i czytelny sposób. Jest to bardzo pomocne, gdy mamy kilka klas modeli z wieloma parametrami. Możemy uczynić niektóre z nich opcjonalnymi lub wymaganymi, a przy tym nie zmuszamy użytkownika do używania określonej kolejności (jak w konstruktorze). Używając wzorca Builder otrzymujemy elegancki łańcuch metod. Najczęstszym zastosowaniem jest klasa AlertDialog.Builder():

new AlertDialog.Builder(this)
.setTitle("Design Patterns")
.setMessage("Builder is awesome")
.create();

Jak możemy stworzyć klasę Builder na własny użytek?

Builder w praktyce

Załóżmy, że mamy jakąś klasę modelu dla użytkownika:

public class User {
private String firstName;
private String lastName;
private int age;
}

I zamiast tworzyć obiekty tej klasy za pomocą konstruktorów, chcemy je tworzyć za pomocą wzorca Builder w ten sposób:

new User.Builder()
.setFirstName("Leonardo")
.setLastName("da Vinci")
.setAge(67)
.create();

Jak możemy to zrobić? Po pierwsze, musimy stworzyć klasę Builder wewnątrz klasy User, która będzie posiadała metody budujące nasz obiekt. Kluczem do posiadania metod łańcuchowych jest to, że metody budujące zwracają klasę Builder. Spójrz na przykład:

static class Builder {
private String firstName;
private String lastName;
private int age;
public Builder setFirstName(final String firstName) {
this.firstName = firstName;
return this;
}
public Builder setLastName(final String lastName) {
this.lastName = lastName;
return this;
}
public Builder setAge(final int age) {
this.age = age;
return this;
}
public User create() {
return new User(this);
}
}

Dla każdego parametru mamy setter – różnica polega na tym, że te metody zwracają typ Builder. Na końcu mamy metodę, która używa konstruktora z klasy User i zwraca obiekt User – tutaj jest miejsce, gdzie nasz bałagan jest ukryty.

Następnie musimy stworzyć konstruktor ze wszystkimi parametrami w klasie modelu User :

public class User {
private String firstName;
private String lastName;
private int age;
private User(final Builder builder) {
firstName = builder.firstName;
lastName = builder.lastName;
age = builder.age;
}
}

Important thing here is that the User constructor is private, so it can’t be accessed from the other class and we must use Builder to create new object.

Oczywiście możemy sprawić, że niektóre z parametrów będą wymagane (na razie wszystkie są opcjonalne) poprzez modyfikację naszej metody create() i rzucenie kilku wyjątków, np.:

public User create() {
User user = new User(firstName, lastName, age);
if (user.firstName.isEmpty()) {
throw new IllegalStateException(
"First name can not be empty!");
}
return user;
}

To wszystko. W ten sposób stworzyliśmy naszą User.Builder()klasę!

Builder – protip

Jeśli byłeś wystarczająco cierpliwy, aby przebrnąć przez cały wpis na blogu, mam dla Ciebie jedną wskazówkę dotyczącą wzorca Builder: możesz wygenerować całą klasę Builder za pomocą IntelliJ!

Wszystko, co musisz zrobić, to umieścić kursor na konstruktorze w swojej klasie i wybrać Refactor -> Replace Constructor with Builder z menu kontekstowego. Klasa konstruktora ze wszystkimi metodami zostanie automatycznie wygenerowana, gotowa do użycia.

Możesz przeczytać więcej tutaj: IntelliJ: Replace Constructor with Builder

Podsumowanie

Wzorzec Builder to świetne podejście, nie tylko dla klas modelowych, ale dla każdego obiektu, który ma więcej niż trzy lub cztery parametry. Przy odrobinie dodatkowej pracy, możemy zwiększyć czytelność naszego kodu. Wzorce projektowe są uznawane za najlepsze praktyki, więc jest to duża zaleta, jeśli znasz kilka z nich, a Builder jest dobrym wzorcem na początek.