Patterns in Kotlin

Like all languages, implementing common patterns is an important aspect and will usually become important at some point in your learning pipeline. Usually, when you have mastered the basics of the language and feel safe enough to think in that language.

Builder Pattern

One of the most common, particularly in the Java universe. Since Java does not support named arguments and default values for method or constructor arguments, the builder is very handy to initialize complex objects with many properties. It usually looks like:

var foo = MyClass.Builder(param1, param2).param3("foo").param4("bar").build();

The class requires two mandatory parameters in its constructors (param1 and param2) and two optionals. For param3 and param4, the class usually sets default values, but you can override them in the Builder.

In Kotlin, you would instantiate such an object with

Kotlin Class with Builder
class Book private constructor(val isbn: String = "", val title: String = "",
  val author: String? = "Unknown", val genre: Genre? = Genre.NONE) {

  enum class Genre {
    /** incomplete */
    FICTION, NONFICTION, DRAMA, PROSE, POETRY, SCIENCE, NONE
  }

  override fun toString(): String {
    return Clique.parser().parse("""
      [yellow, bold]ISBN: [/]  $isbn
      [yellow, bold]Title:[/]  $title
      [yellow, bold]Author[/]: $author
      [yellow, bold]Genre:[/]  $genre
    """).trimIndent()
  }

  data class Builder(val isbn: String, val title: String,
    var author: String? = "Unknown", var genre: Genre? = Genre.NONE) {

    fun author(author: String) = apply { this.author = author }
    fun genre(genre: Genre) = apply { this.genre = genre }

    fun build(): Book = Book(this.isbn, this.title, this.author, this.genre)
  }
}
A Java Builder
package cc.subspace.kweather;
import io.github.kusoroadeolu.clique.Clique;

class JavaBook {
    enum Genre {
        FICTION, NONFICTION, DRAMA, PROSE, POETRY, SCIENCE, NONE
    }
    private String isbn  = null;
    private String title = null;
    private String author = "Unknown";
    private Genre  genre = Genre.NONE;

    private JavaBook(final String isbn, final String title, final String author, final Genre genre) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.genre = genre;
    }

    public String toString() {
        return Clique.parser().parse(String.format(
        """
        [yellow, bold]ISBN: [/]  %s
        [yellow, bold]Title:[/]  %s
        [yellow, bold]Author[/]  %s
        [yellow, bold]Genre:[/]  %s
        """, isbn,title, author, genre)).stripIndent();
    }

    static class Builder {
        private String isbn  = null;
        private String title = null;
        private String author = "Unknown";
        private Genre  genre = Genre.NONE;

        Builder(final String isbn, final String title) {
            this.isbn = isbn;
            this.title = title;
        }

        public Builder author(final String author) { this.author = author; return this; }
        public Builder genre(final Genre genre) { this.genre = genre; return this; }

        public JavaBook build() {
            return new JavaBook(isbn, title, author, genre);
        }
    }
}

As you can see from the two code examples, the Kotlin code is much shorter und much simpler and the Builder is totally optional, because in reality, you could simply:

  • Make the Book() constructor public

  • Instantiate the class with val b = Book("978-3-16-148410-0", "A Kotlin Book", genre = Book.Genre.SCIENCE) and be done. This would be faster, smaller and easier to read compared to the fluid interface used by the typical Builder.