Designmönster i Android – Builder

Builder

Builder-mönstret förenklar skapandet av objekt på ett mycket rent och lättläst sätt. Det är mycket användbart när vi har några modellklasser med många parametrar. Vi kan göra vissa av dem valfria eller obligatoriska, och vi tvingar inte användaren att använda en viss ordning (som i konstruktören). Genom att använda Builder-mönstret får vi en elegant kedja av metoder. Den vanligaste användningen är i AlertDialog.Builder() klassen:

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

Hur kan vi skapa Builder klassen för vår egen användning?

Builder i praktiken

Vi antar att vi har en viss modellklass för användaren:

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

Och i stället för att skapa objekt av den här klassen med hjälp av konstruktörer vill vi skapa dem med hjälp av Builder mönstret så här:

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

Hur kan vi göra det? För det första måste vi skapa Builder-klassen inuti User-klassen som kommer att ha metoderna för att bygga vårt objekt. Nyckeln till att ha kedjemetoder är att byggmetoderna returnerar Builder-klassen. Titta på exemplet:

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);
}
}

För varje parameter har vi en setter – skillnaden är att dessa metoder returnerar Builder typ. I slutet har vi en metod som använder konstruktör från User-klassen och returnerar User-objektet – här är platsen där vår röra hålls gömd.

Därefter måste vi skapa konstruktör med alla parametrar i modellklassen 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;
}
}

Viktigt här är att User-konstruktören är privat, så den kan inte nås från den andra klassen och vi måste använda Builder för att skapa nytt objekt.

Självklart kan vi göra några av parametrarna obligatoriska (för tillfället är alla frivilliga) genom att ändra vår create() metod och kasta några undantag, t.ex.:

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;
}

Det är allt. På så sätt skapade vi vår User.Builder() klass!

Builder – protip

Om du hade tålamod nog att ta dig igenom hela blogginlägget har jag ett tips till dig angående Builder-mönstret: du kan generera hela Builder-klassen med hjälp av IntelliJ!

Det enda du behöver göra är att placera careten på konstruktorn i din klass och välja Refactor -> Replace Constructor with Builder på kontextmenyn. Konstruktörsklassen med alla metoder genereras automatiskt och är redo att användas.

Du kan läsa mer här: IntelliJ: Replace Constructor with Builder

Slutsats

Builder-mönstret är ett bra tillvägagångssätt, inte bara för modellklasser utan för alla objekt som har mer än tre eller fyra parametrar. Med lite extra arbete kan vi öka läsbarheten i vår kod. Designmönster erkänns som bästa praxis, så det är en stor fördel om du känner till några av dem och Builder är ett bra mönster att börja med.