Behavioral Design Pattern
Kotlin Design Patterns: Strategy Explained
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
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:
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.
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:
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)