Modèles de conception créatifs dans Kotlin: Constructeur

Modèles de création dans Kotlin: Constructeur

1. introduction

Dans cet article rapide, nous verrons comment implémenter le modèle de conception Builder dans Kotlin.

2. Modèle de constructeur

Le modèle Builder est celui que les gens utilisent souvent mais qu'ils créent rarement eux-mêmes.

C’est formidable de gérer la construction d’objets qui peuvent contenir de nombreux paramètres et lorsque nous voulons rendre l’objet immuable une fois que nous avons terminé de le construire.

Pour en savoir plus, consultez notre didacticiel sur les modèles de conception créativehere.

3. la mise en oeuvre

Kotlin fournit de nombreuses fonctionnalités utiles telles que les paramètres nommés et par défaut,apply() etdata class qui évitent l'utilisation de l'implémentation classique du modèle Builder.

Pour cette raison, nous verrons d'abord une implémentation de style Java classique, puis une forme abrégée de style Kotlin.

3.1. Implémentation de style Java

Commençons par créer une classe -FoodOrder - qui contient des champs en lecture seule, car nous ne voulons pas que les objets externes y accèdent directement:

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

Notez quethe constructor is private so that only the nested Builder class can access in it.

Passons maintenant à la création de la classe imbriquée qui sera utilisée pour créer des objets:

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

Comme on le voit,our Builder has the same fields as the outer class. For each outer field, we have a matching setter method.

Au cas où nous aurions un ou plusieurs champs obligatoires, au lieu d’utiliser des méthodes de définition, faisons en sorte qu’un constructeur les définisse.

Notez que nous utilisons la fonctionapply afin de prendre en charge l'approchefluent design.

Enfin, avec la méthodebuild, nous appelons le constructeurFoodOrder.

3.2. Mise en œuvre de style Kotlin

Afin de tirer pleinement parti de Kotlin, nous devons revoir certaines des meilleures pratiques auxquelles nous sommes habitués en Java. Beaucoup d'entre eux peuvent être remplacés par de meilleures alternatives.

Voyons comment on peut écrire du code Kotlin idiomatique:

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 comes with named and default parameters help to minimize the number of overloads et améliore la lisibilité de l'appel de la fonction.

Nous pouvons également tirer parti de la structure de classe de données de Kotlin que nous explorons plus en détail dans un autre tutorielhere.

Enfin, ainsi que dans l'implémentation de style Java,apply() est utile pour implémenter des setters fluents.

4. Exemple d'utilisation

Voyons brièvement comment créer des objetsFoodOrder à l'aide de ces implémentations de modèle Builder:

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

5. Conclusion

Le modèle de constructeur résout un problème très courant dans la programmation orientée objet: créer de manière flexible un objet immuable sans écrire de nombreux constructeurs.

Lorsque l'on considère un constructeur, nous devons nous concentrer sur la complexité ou non de la construction. Si nous avons des modèles de construction trop simples, l'effort de création de notre objet constructeur flexible peut dépasser de loin les avantages.

Comme toujours, le code est disponibleover on Github.