Creational Design Patterns in Kotlin: Builder

1. Introduzione

In questo rapido articolo, vedremo come implementare il Builder Design Pattern in Kotlin.

2. Pattern Builder

Il modello Builder è quello che le persone usano spesso ma raramente creano da sole.

È fantastico gestire la creazione di oggetti che possono contenere molti parametri e quando vogliamo rendere l'oggetto immutabile una volta che abbiamo finito di costruirlo.

Per saperne di più, dai un'occhiata al nostro tutorial su Creational Design Patterns qui.

3. Implementazione

Kotlin fornisce molte funzioni utili come parametri denominati e predefiniti, apply () e classe di dati che evitano l'uso dell'implementazione del pattern Builder classico.

Per questo motivo, vedremo prima un'implementazione classica in stile Java e poi una forma abbreviata in stile più Kotlin.

3.1. Implementazione in stile Java

Cominciamo a creare una classe - FoodOrder - che contiene campi di sola lettura poiché non vogliamo che gli oggetti esterni accedano direttamente ad essi:

class FoodOrder private constructor(builder: FoodOrder.Builder) { val bread: String? val condiments: String? val meat: String? val fish: String? init { this.bread = builder.bread this.condiments = builder.condiments this.meat = builder.meat this.fish = builder.fish } class Builder { // builder code } }

Si noti che il costruttore è privato in modo che solo la classe Builder annidata possa accedervi.

Passiamo ora alla creazione della classe annidata che verrà utilizzata per costruire oggetti:

class Builder { var bread: String? = null private set var condiments: String? = null private set var meat: String? = null private set var fish: String? = null private set fun bread(bread: String) = apply { this.bread = bread } fun condiments(condiments: String) = apply { this.condiments = condiments } fun meat(meat: String) = apply { this.meat = meat } fun fish(fish: String) = apply { this.fish = fish } fun build() = FoodOrder(this) } 

Come si vede, il nostro Builder ha gli stessi campi della classe esterna. Per ogni campo esterno, abbiamo un metodo setter corrispondente.

Nel caso in cui abbiamo uno o più campi obbligatori, invece di usare i metodi setter, facciamo in modo che un costruttore li imposti.

Nota che stiamo usando la funzione di applicazione per supportare l'approccio di progettazione fluente.

Infine, con il metodo build , chiamiamo il costruttore FoodOrder .

3.2. Implementazione in stile Kotlin

Per sfruttare appieno Kotlin, dobbiamo rivisitare alcune best practice a cui ci siamo abituati in Java. Molti di loro possono essere sostituiti con alternative migliori.

Vediamo come scrivere codice idiomatico Kotlin:

class FoodOrder private constructor( val bread: String?, val condiments: String?, val meat: String?, val fish: String?) { data class Builder( var bread: String? = null, var condiments: String? = null, var meat: String? = null, var fish: String? = null) { fun bread(bread: String) = apply { this.bread = bread } fun condiments(condiments: String) = apply { this.condiments = condiments } fun meat(meat: String) = apply { this.meat = meat } fun fish(fish: String) = apply { this.fish = fish } fun build() = FoodOrder(bread, condiments, meat, fish) } }

Kotlin viene fornito con parametri denominati e predefiniti che aiutano a ridurre al minimo il numero di sovraccarichi e migliorare la leggibilità dell'invocazione della funzione.

Possiamo anche trarre vantaggio dalla struttura della classe di dati di Kotlin che esploreremo di più in un altro tutorial qui.

Infine, così come nell'implementazione in stile Java, apply () è utile per implementare setter fluenti.

4. Esempi di utilizzo

In breve, diamo un'occhiata a come costruire oggetti FoodOrder usando queste implementazioni del pattern Builder:

val foodOrder = FoodOrder.Builder() .bread("white bread") .meat("bacon") .condiments("olive oil") .build() 

5. conclusione

Il Builder Pattern risolve un problema molto comune nella programmazione orientata agli oggetti di come creare in modo flessibile un oggetto immutabile senza scrivere molti costruttori.

Quando si considera un costruttore, dovremmo concentrarci sul fatto che la costruzione sia complessa o meno. Se abbiamo schemi di costruzione troppo semplici, lo sforzo di creare il nostro oggetto di costruzione flessibile potrebbe superare di gran lunga il vantaggio.

Come sempre, il codice è disponibile su Github.