DEV Community

Cover image for Gang of Four Patterns in Kotlin
Lovis
Lovis

Posted on

Gang of Four Patterns in Kotlin

Kotlin is getting more and more relevant. How would common design patterns implemented in Kotlin look like?
Inspired by Mario Fusco's Talk/Blog posts/Repository "From GoF to lambda", I decided to implement some of the most famous design patterns in computer science in Kotlin!

The goal is not to simply implement the patterns, though. Since Kotlin supports object oriented programming and is interoperable with Java, I could just copy-auto-convert every java class in Mario's repository (doesn't matter whether it's the "traditional" or the "lambda" examples) and it would still work!
It's also important to note that these patterns where discovered to overcome the shortcomings of how imperative programming languages were designed in the 90s (C++ in particular). Many modern languages provide features to overcome these issues without writing extra code or falling back to patterns.

That's why —just like Mario in the g ∘ f repository— I will try to find a simpler, easier or more idiomatic way to solve the same problem each pattern is solving.

If you don't like reading explanations, you can directly jump to my github repository


As you might know, there are three types of (gof) patterns: structural, creational and behavioral patterns.
First up are the structural patterns. This is tough, because structural patterns are about - structure! How could I implement a structure with a different structure? I can't. An exception is the Decorator. While it's technically just about structure, its usage is more about behavior or responsibilities.

Structural Patterns

Decorator

Attach additional responsibilities to an object dynamically

Let's say I want to decorate a class Text with some text effects:

class Text(val text: String) {
    fun draw() = print(text)
}
Enter fullscreen mode Exit fullscreen mode

If you know the pattern, you also know that a set of classes has to be created in order to "decorate" (that is extending the behavior) the Text class.
These extra classes can be avoided in Kotlin by using extension functions:

fun Text.underline(decorated: Text.() -> Unit) {
    print("_")
    this.decorated()
    print("_")
}

fun Text.background(decorated: Text.() -> Unit) {
    print("\u001B[43m")
    this.decorated()
    print("\u001B[0m")
}
Enter fullscreen mode Exit fullscreen mode

With these extension functions, I can now create a new Text and decorate its draw method without creating other classes:

Text("Hello").run {
    background {
        underline {
            draw()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

If you run this from the command line, you will see the text "_Hello_" with colored background (if your terminal supports ansi colors).
Compared to the original Decorator pattern, there is one drawback: I can not pass "pre-decorated" objects around, since there is no Decorator class anymore.
To solve this I can use functions again. Functions are first-class citizens in Kotlin, so I can pass those around instead.

fun preDecorated(decorated: Text.() -> Unit): Text.() -> Unit {
    return { background { underline { decorated() } } }
}
Enter fullscreen mode Exit fullscreen mode

Creational

Builder

Separate the construction of a complex object from its representation so that the same construction process can create different representations

The Builder pattern is really useful. I can avoid variable-heavy constructors and easily re-use predefined setups. Kotlin supports this pattern out of the box with the apply extension function.
Consider a class Car:

class Car() {
    var color: String = "red"
    var doors = 3
}
Enter fullscreen mode Exit fullscreen mode

Instead of creating a separate CarBuilder for this class, I can now use the apply (also works as well) extension to initialize the car:

Car().apply {
    color = "yellow"
    doors = 5
}
Enter fullscreen mode Exit fullscreen mode

Since functions can be stored in a variable, this initialization could also be stored in a variable. This way, I can have pre-configured Builder-functions, e.g. val yellowCar: Car.() -> Unit = { color = "yellow" }

Prototype

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype

In Java, prototyping could theoretically be implemented using the Cloneable interface and Object.clone(). However, clone is broken, so we should avoid it.
Kotlin fixes this with data classes.
When I use data classes, I get equals, hashCode, toString and copy for free. By using copy, it's possible to clone the whole object and optionally change some of the new object's properties.

data class EMail(var recipient: String, var subject: String?, var message: String?)
...

val mail = EMail("abc@example.com", "Hello", "Don't know what to write.")

val copy = mail.copy(recipient = "other@example.com")

println("Email1 goes to " + mail.recipient + " with subject " + mail.subject)
println("Email2 goes to " + copy.recipient + " with subject " + copy.subject)
Enter fullscreen mode Exit fullscreen mode

Singleton

Ensure a class only has one instance, and provide a global point of access to it

Although the Singleton is considered an anti-pattern these days, it has its usages (I won't discuss this topic here, just use it with caution).
Creating a Singleton in Java needs a lot of ceremony, but Kotlin makes it easy with object declarations.

object Dictionary {
    private val definitions = mutableMapOf<String, String>

    fun addDefinition(word: String, definition: String) {
        definitions.put(word.toLowerCase(), definition)
    }

    fun getDefinition(word: String): String {
        return definitions[word.toLowerCase()] ?: ""
    }
}
Enter fullscreen mode Exit fullscreen mode

Using the object keyword here will automatically create a class Dictionary and a single instance of it. The instance is created lazily, so it's not created until it is actually used.
The object is accessed like static functions in java:

val word = "kotlin"
Dictionary.addDefinition(word, "an awesome programming language created by JetBrains")
println(word + " is " + Dictionary.getDefinition(word))
Enter fullscreen mode Exit fullscreen mode

Behavioral

Template Method

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses

This pattern makes use of class hierarchies as well. You define an abstract method and call it somewhere inside the base class. The implementation is handled by the subclasses.

//java
public abstract class Task {
        protected abstract void work();
        public void execute(){
            beforeWork();
            work();
            afterWork();
        }
    }
Enter fullscreen mode Exit fullscreen mode

I could now derive a concrete Task, that actually does something in work.
Similar to how the Decorator example uses extension functions, my Template Method approach uses a top-level function.

//kotlin
fun execute(task: () -> Unit) {
    val startTime = System.currentTimeMillis() //"beforeWork()"
    task()
    println("Work took ${System.currentTimeMillis() - startTime} millis") //"afterWork()"
}

...
//usage:
execute {
    println("I'm working here!")
}
Enter fullscreen mode Exit fullscreen mode

As you can see, there is no need to have a class at all! One could argue, that this now resembles the Strategy pattern, which is probably correct. But then again, Strategy and Template Method are solving very similar problems (if not the same).

Strategy

Define a family of algorithms, encapsulate each one, and make them interchangeable

Let's say I have a Customer that pays a certain fee per month. This fee can be discounted. Instead of having a subclass of Customer for each discounting-strategy, I use the Strategy pattern.

class Customer(val name: String, val fee: Double, val discount: (Double) -> Double) {
    fun pricePerMonth(): Double {
        return discount(fee)
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice that I'm using a function (Double) -> Double instead of an interface for the strategy. To make this more domain-specific, I could also declare a type alias, without losing the flexibility of an higher-order-function: typealias Discount = (Double) -> Double.
Either way, I can define multiple strategies for calculating the discount.

val studentDiscount = { fee: Double -> fee/2 }
val noDiscount = { fee: Double -> fee }
...

val student = Customer("Ned", 10.0, studentDiscount)
val regular = Customer("John", 10.0, noDiscount)

println("${student.name} pays %.2f per month".format(student.pricePerMonth()))
println("${regular.name} pays %.2f per month".format(regular.pricePerMonth()))
Enter fullscreen mode Exit fullscreen mode

Iterator

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Writing an Iterator is a rare task. Most of the time, it's easier and more convenient to wrap a List and implement the Iterable interface.
In Kotlin, iterator() is an operator function. This means that when a class defines a function operator fun iterator(), it can be iterated using a for loop (no interface needed). That's particularly cool because it works also with extension functions. That's right - by using an extension function, I can make every object iterable. Consider this example:

class Sentence(val words: List<String>)
...
operator fun Sentence.iterator(): Iterator<String> = words.iterator()
Enter fullscreen mode Exit fullscreen mode

I can now iterate over a Sentence. This also works if I'm not the owner of the class.

More patterns

I mentioned a few patterns, but those are not all Gang of Four patterns. As I said in the introduction, especially the structural patterns are hard or impossible to write in a different way than in Java. Some other patterns can be found in the repository. I'm open for feedback and pull-requests ☺.

I hope this post gave you an idea on how Kotlin can result in different approaches to well known problems.

Make sure to check out the second part of this post: Gang of Four Patterns in Kotlin - Slight Return

One last thing I want to mention is, that the java-to-kotlin ratio in the repository is ~⅓ Kotlin and ~⅔ Java, although both versions do the same thing 🙃


The cover image was taken from stocksnap.io

Top comments (23)

Collapse
 
mariofusco profile image
Mario Fusco

Great post, I just have one note: for what regards the Decorator example, you're using extension methods, which is a very desirable feature of Kotlin and other languages like C#. That's nice, but the point of the corresponding example in my Java 8 implementation was a bit different and probably more fundamental. The Decorator pattern as demonstrated in GoF is just a very convoluted way to achieve functions composition. Using functions composition, which is not a specific language feature, but a corner stone of functional programming, and then available in any language with a minimal functional support including Java 8, you can achieve the same result in a more elegant way. Conversely what you did is reimplementing the Decorator pattern literally and in its original form, only in a more concise way using Kotlin's better expressiveness. Both solutions are valid, but still I believe that the one using function composition is more functional-ish, general and possibly even easier to read and to implement.

Collapse
 
marioariasc profile image
Mario Arias

My library (funKTionale) implements function composition for Kotlin (among other goodies) github.com/MarioAriasC/funKTionale...

Currently working on a blog post series covering many features

Collapse
 
qwertukg profile image
Daniil Rakhmatulin

its hard to read infix fun in this case

Collapse
 
lovis profile image
Lovis

Really looking forward to your posts!

Thread Thread
 
lovis profile image
Lovis • Edited

Here it is, if anyone is interested and drops by:
hackernoon.com/funktionale-functio...

Collapse
 
lovis profile image
Lovis

Hey Mario, first of all: thanks for your kind words!
It's definitely true that function composition would be the better solution here. I used extensions because it's "pure" Kotlin. I didn't want to copy your examples and solutions, either. Thought that would be boring :-)

Collapse
 
qwertukg profile image
Daniil Rakhmatulin • Edited

Really good post. short and clean - like as a Kotlin style ;)
But i think builder could be opened more. Your example is good, but i mean the using of builder is much more then just setting of a properties. Most interesting example, is then u want to pass some code like as parameter. Something from my DSL for selenium SeleniumBuilder:

inline fun <T : WebDriver> driver(driver: T, init: T.() -> Unit) {
    try {
        driver.init()
    } finally {
        driver.close()
    }
}

Or more colorful example, like as u show, if u want to calc execution time of some code:

inline fun calcExecTime(init: () -> Unit): Float {
    val start = System.nanoTime()
    init()
    return (System.nanoTime() - start) / 1000000000F
}
val resultTime = calcExecTime {
    // do anything here
}
Collapse
 
sorokod profile image
David Soroko

Using apply for post constructor changes is nice but I wouldn't call it a builder. With the approach you are describing an instance is created first, with empty constructor, and then modified in the apply{} block. This means that

  1. Default values are required

  2. It is not possible to ensure that an instance exists only if the parameters are valid

Collapse
 
lovis profile image
Lovis

thank you!

yeah, those type save builders / DSLs are nice. But they are a topic on their own, and and wanted to keep it "basic" :-)

I would not consider calcExecTime a builder, since it does not create anything.

btw a method like this already exists in the standard lib - its called measureTimeMillis :-)

Collapse
 
qwertukg profile image
Daniil Rakhmatulin

actually yes, its build nothing) sorry for disorientation, but I want to show many other features, who can be provided by lambda with receiver.

Collapse
 
anitas3791 profile image
Anita Singh

Loved this post! I just started using Kotlin in our Android app two months ago and feel that I am writing Java-ey Kotlin sometimes. These are great patterns to keep in mind, looking forward to using them!

Collapse
 
geraldcroes profile image
geraldcroes

I found your article while I was looking for patterns examples in Kotlin ... loved it (and loved its [more advanced] sequel)

The factory was missing, so (even if it's quite straight forward) I've written an article about it.

I wonder if you have other (better) solutions.
medium.com/the-coding-matrix/kotli...

Collapse
 
uweschaefer profile image
Uwe Schaefer

Minor typo:

Dictionary.addWord(word, "an awesome programming language created by JetBrains")

should be

Dictionary.addDefinition(word, "an awesome programming language created by JetBrains")

Collapse
 
lovis profile image
Lovis

Thanks, Uwe! Fixed.

Collapse
 
kenkyee profile image
Ken Yee

For the Builder pattern, you can also use a constructor w/ named params...seems more Kotlin idiomatic IMHO...

Collapse
 
lovis profile image
Lovis • Edited

I agree for this particular example, but consider a class with more properties.
You generally don't want a constructor (or any other method) with five or more parameters.

The builder can also do more than just building the object. E.g. validating correct property-combinations, which is not something you should do inside a constructor.

Collapse
 
programmerr47 profile image
Michael

I think one of the problems with your approach is theoretically possibility to have broken data, since constuction of object is separated from setting up field.

Approach with naming constructor parameters or more stable and looks like builder, since all preparations are happen in constructor of object. And if it will fail, you just will not receive a new object.

Thread Thread
 
lovis profile image
Lovis

Sure, but separating construction from representation is the whole point of the Builder pattern.

Collapse
 
gabin278 profile image
Andrey Derkach

Thanks! Great article. Looking forward for the next useful ones

Collapse
 
nicc_fsc profile image
Jeremy Feng

I am a game programmer in China. In my country, few people use Kotlin for programming, What do people think of Kotlin in your country? Do many people use it to develop Android?

Collapse
 
lovis profile image
Lovis

It's still a niche language here in Germany.
Most people who use it use it for Android.
I'm trying to change that, though. E.g. I founded the Kotlin User Group Hamburg, to "spread the word" 😉

Collapse
 
ben profile image
Ben Halpern

I'm really enjoying your Kotlin posts. I'm relying on them to keep up as I consider getting into Kotlin, thanks a lot.

Collapse
 
lovis profile image
Lovis

Thank you! Great to hear that!