Behavioral Design Pattern

Kotlin Design Patterns: Strategy Explained

Michal Ankiersztajn
ProAndroidDev
Published in
3 min readApr 24, 2024

--

Purpose of the pattern

Selection of algorithm in runtime. Instead of implementing algorithms directly in place of use, create a separate class for each algorithm and treat them like a family. Depending on the conditions, you’ll want to swap the algorithm you used.

What do we get from that?

  • Reusability: once you create a Strategy, you can use it in multiple places and projects.
  • Flexibility: swapping algorithms at runtime.
  • Single Responsibility: algorithm classes are separated from the rest of the code and have a single purpose.
  • Open/Closed: introduce new Strategy algorithms without changing the existing ones.

Implementation

Strategy class diagram

Here, you can see how to implement it. We have a Strategy interface with an algorithm function that ConcreteStrategies implement. There’s a clear separation between Strategies and Context that is aware of the current algorithm.

Consider the following Context modification if you don’t need to store Strategy as a variable and update it but rather pass new strategies:

Alternative context

This way, we avoid the mutability of Strategy and make the program a bit easier to debug, but we are now forced to pass in Strategy each time we want to doSomething .

Example

Your task is to implement a Location System. You’ll have to support Car and Bike. However, you’ll likely have to support other means of transport as well. This is a perfect place to use Strategy and swap path creation algorithms in runtime.

Location System Strategy Diagram

Let’s start by creating our Strategies as they’re fully isolated:

interface PathStrategy {
fun getPath(): String
}

object CarPathStrategy : PathStrategy {
override fun getPath() = "Car path"
}

object BikePathStrategy : PathStrategy {
override fun getPath() = "Bike path"
}

For simplicity our PathStrategy concrete classes will return a String, but you should implement your algorithm inside that function. Also, because they don’t need any additional dependencies, they can be written as objects that hold the algorithm function.

Now, we need a PathContext :

class PathContext(
private var strategy: PathStrategy,
) {
fun getPath() {
// Just printing path for simplicity
// In real app you would do something with the result here
println(strategy.getPath())
}

fun setStrategy(strategy: PathStrategy) {
this.strategy = strategy
}
}

Now that we have everything in place let’s look at the usage:

fun main() {
val pathContext = PathContext(CarPathStrategy)
pathContext.getPath() // Car path

pathContext.setStrategy(BikePathStrategy)
pathContext.getPath() // Bike path
}

Thanks for reading! Please clap if you learned something, and follow me for more!

Learn more about design patterns:

Design Patterns In Kotlin

17 stories

Based on the book:

“Wzorce projektowe : elementy oprogramowania obiektowego wielokrotnego użytku” — Erich Gamma Autor; Janusz Jabłonowski (Translator); Grady Booch (Introduction author); Richard Helm (Author); Ralph Johnson (Author); John M Vlissides (Author)

--

--