Understanding the Strategy pattern

How should two objects be compared when sorting? What action should be performed when a game controller's button is pressed? How should a computer-controlled player choose which chess piece to move? All of these questions are examples of problems where the Strategy pattern could be applied to make our algorithms and programs more flexible and easier to maintain.

Let's take the example of writing a bot to play chess. We may want to create several different levels of difficulty. Let's see how to do this:

  1. We could do this by defining a common interface such as the following:
interface BotBehavior {
fun generateBoardState(currentState: Array<Array<Int>>)
}
  1. We could then define different types of specific behavior by implementing that interface in concrete behavior classes such as the following:
object EasyBot : BotBehavior {
override fun generateBoardState(currentState: Array<Array<Int>>) {
// pick first available action
}
}

object DifficultBot : BotBehavior {
override fun generateBoardState(currentState: Array<Array<Int>>) {
// perform look ahead search
}
}
  1. With these different behaviors in mind, we might then create a Chess class that takes BotBehavior and delegates to that behavior in its implementation of the game:
class Chess(var behavior: BotBehavior) {
fun play() {
// game loop
// relies on behavior
}

fun pause() { }
}
  1. Finally, we can create an instance of Chess with a specific encapsulated behavior, pause the game, and update the behavior. Once the behavior is swapped out, the behavior of the Chess instance as a whole may be very different. The following code shows this:
fun main() {
val game = Chess(EasyBot)
game.play()
game.pause()
game.behavior = DifficultBot
game.play()
}

By using the Strategy pattern to encapsulate these behaviors, we can change the overall behavior of the game without modifying its code, and this can be updated at runtime, making it more flexible still.

In the next section, we'll look at using higher-order functions to define our strategies with Kotlin.