Шаблоны креативного дизайна в Котлине: Builder

Шаблоны креативного дизайна в Котлине: Строитель

1. Вступление

В этой быстрой статье мы увидим, как реализовать шаблон проектирования Builder в Kotlin.

2. Образец Строителя

Шаблон Builder - это тот, который люди часто используют, но редко создают самостоятельно.

Замечательно обрабатывать построение объектов, которые могут содержать множество параметров, и когда мы хотим сделать объект неизменным после того, как построим его.

Чтобы узнать больше, ознакомьтесь с нашим руководством по Creational Design Patternshere.

3. Реализация

Kotlin предоставляет множество полезных функций, таких как именованные параметры и параметры по умолчанию,apply() иdata class, что позволяет избежать использования классической реализации шаблона Builder.

По этой причине мы увидим сначала классическую реализацию в стиле Java, а затем более короткую форму в стиле Kotlin.

3.1. Реализация в стиле Java

Давайте начнем создавать один класс -FoodOrder - который содержит поля только для чтения, поскольку мы не хотим, чтобы внешние объекты обращались к ним напрямую:

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

Обратите внимание, чтоthe constructor is private so that only the nested Builder class can access in it.

Теперь перейдем к созданию вложенного класса, который будет использоваться для создания объектов:

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

Как видим,our Builder has the same fields as the outer class. For each outer field, we have a matching setter method.

Если у нас есть одно или несколько обязательных полей, вместо использования методов установки давайте сделаем конструктор, устанавливающий их.

Обратите внимание, что мы используем функциюapply для поддержки подходаfluent design.

Наконец, с помощью методаbuild мы вызываем конструкторFoodOrder.

3.2. Реализация в стиле Котлин

Чтобы в полной мере воспользоваться преимуществами Kotlin, мы должны пересмотреть некоторые лучшие практики, к которым мы привыкли в Java. Многие из них могут быть заменены лучшими альтернативами.

Давайте посмотрим, как мы можем написать идиоматический код 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 comes with named and default parameters help to minimize the number of overloads и улучшите читаемость вызова функции.

Мы также можем воспользоваться структурой классов данных Kotlin, которую мы подробнее исследуем в другом руководствеhere.

Наконец, как и в реализации в стиле Java,apply() полезен для реализации свободно распространяемых сеттеров.

4. Пример использования

Вкратце, давайте посмотрим, как создавать объектыFoodOrder, используя эту реализацию шаблона Builder:

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

5. Заключение

Шаблон Builder решает очень распространенную проблему объектно-ориентированного программирования, заключающуюся в том, как гибко создавать неизменяемый объект без написания множества конструкторов.

Рассматривая застройщика, мы должны сосредоточиться на том, сложна ли конструкция. Если у нас слишком простые шаблоны построения, то усилия по созданию нашего гибкого объекта-строителя могут значительно превысить выгоду.

Как всегда доступен кодover on Github.