Design Patterns in Kotlin — Part 1

Lakshay Parnami
4 min readMar 22, 2023

--

Kotlin is a modern and powerful programming language that has gained popularity in recent years. One of the reasons for its popularity is its compatibility with the Java Virtual Machine (JVM) and Android. Kotlin offers a wide range of features, including null safety, functional programming, and coroutines. It also supports various design patterns that help developers to create robust and maintainable code. In this blog, we will discuss some of the popular design patterns in Kotlin with examples.

Singleton Pattern:

The Singleton Pattern ensures that only one instance of a class is created and provides a global point of access to that instance. In Kotlin, we can create a singleton using the object keyword.

object Singleton {
fun showMessage() {
println("Hello, World!")
}
}

We can call the showMessage() function of the Singleton object as follows:

Singleton.showMessage()

Factory Pattern:

The Factory Pattern is a creational pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. In Kotlin, we can use a companion object to implement the Factory Pattern.

interface Animal {
fun makeSound()
}

class Dog : Animal {
override fun makeSound() {
println("Woof!")
}
}

class Cat : Animal {
override fun makeSound() {
println("Meow!")
}
}

class AnimalFactory {
companion object {
fun createAnimal(type: String): Animal? {
return when (type) {
"dog" -> Dog()
"cat" -> Cat()
else -> null
}
}
}
}

We can use the AnimalFactory to create objects of the Dog or Cat class as follows:

val dog = AnimalFactory.createAnimal("dog")
dog?.makeSound()

val cat = AnimalFactory.createAnimal("cat")
cat?.makeSound()

Observer Pattern:

The Observer Pattern is a behavioral pattern that allows objects to observe changes in other objects and be notified when those changes occur. In Kotlin, we can use the observable and observer properties to implement the Observer Pattern.

class Subject {
val observers = mutableListOf<Observer>()
var state: Int = 0
set(value) {
field = value
notifyObservers()
}
fun attach(observer: Observer) {
observers.add(observer)
}
fun detach(observer: Observer) {
observers.remove(observer)
}
fun notifyObservers() {
for (observer in observers) {
observer.update()
}
}
}

interface Observer {
fun update()
}

class BinaryObserver(private val subject: Subject) : Observer {
override fun update() {
println("Binary String: ${Integer.toBinaryString(subject.state)}")
}
}

class OctalObserver(private val subject: Subject) : Observer {
override fun update() {
println("Octal String: ${Integer.toOctalString(subject.state)}")
}
}

class HexObserver(private val subject: Subject) : Observer {
override fun update() {
println("Hex String: ${Integer.toHexString(subject.state)}")
}
}

We can use the Subject class and the BinaryObserver, OctalObserver, and HexObserver classes to implement the Observer Pattern as follows:

val subject = Subject()
BinaryObserver(subject)
OctalObserver(subject)
HexObserver(subject)

subject.state = 10
subject.state = 15

Builder Pattern:

The Builder Pattern is a creational pattern that separates the construction of a complex object from its representation so that the same construction process can create different representations. It allows you to create objects step-by-step by providing a flexible and easy-to-use interface.

Here’s an example implementation of the Builder Pattern in Kotlin:

class House(private val foundation: String, private val walls: String, private val roof: String, private val finishing: String) {
override fun toString(): String {
return "House with $foundation foundation, $walls walls, $roof roof, and $finishing finishing"
}
}

interface HouseBuilder {
fun buildFoundation()
fun buildWalls()
fun buildRoof()
fun addFinishing()
fun getHouse(): House
}

class ConcreteHouseBuilder : HouseBuilder {
private lateinit var foundation: String
private lateinit var walls: String
private lateinit var roof: String
private lateinit var finishing: String

override fun buildFoundation() {
foundation = "concrete"
}

override fun buildWalls() {
walls = "brick"
}

override fun buildRoof() {
roof = "slate"
}

override fun addFinishing() {
finishing = "paint"
}

override fun getHouse(): House {
return House(foundation, walls, roof, finishing)
}
}

class HouseDirector(private val builder: HouseBuilder) {
fun construct() {
builder.buildFoundation()
builder.buildWalls()
builder.buildRoof()
builder.addFinishing()
}
}

fun main() {
val builder = ConcreteHouseBuilder()
val director = HouseDirector(builder)
director.construct()
val house = builder.getHouse()
println(house)
}

In this example, we define a House class that represents the final product. We also define a HouseBuilder interface that defines the construction steps and a ConcreteHouseBuilder class that implements the HouseBuilder interface and provides the implementation details for the construction steps.

Strategy Pattern:

This pattern allows you to define a family of algorithms, encapsulate each one as an object, and make them interchangeable at runtime. In Kotlin, this can be implemented using lambda expressions or functional interfaces.

Here’s an example implementation of the Strategy Pattern in Kotlin:

interface AttackStrategy {
fun attack()
}

class SwordAttackStrategy : AttackStrategy {
override fun attack() {
println("Attacking with sword")
}
}

class AxeAttackStrategy : AttackStrategy {
override fun attack() {
println("Attacking with axe")
}
}

class BowAttackStrategy : AttackStrategy {
override fun attack() {
println("Attacking with bow")
}
}

class Player(private val attackStrategy: AttackStrategy) {
fun attack() {
attackStrategy.attack()
}
}

fun main() {
val player = Player(SwordAttackStrategy())
player.attack()

player.attackStrategy = AxeAttackStrategy()
player.attack()

player.attackStrategy = BowAttackStrategy()
player.attack()
}

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Lakshay Parnami
Lakshay Parnami

Written by Lakshay Parnami

Hi, I’m Lakshay Parnami. I’m into Mobile Application Development, I work on Android, Flutter & React Native. I’m currently learning iOS development.

No responses yet

Write a response