The strategy pattern involves only a few components:
- Context objects, which will have a Strategy member
- Strategy implementations that can be swapped at runtime
Let's attempt to generalize this implementation via protocols.
Strategy is the algorithm that runs the code, and that will be swapped at runtime. At a high level, a strategy is only dependent on its algorithm ReturnType:
protocol Strategy {
associatedtype ReturnType
func run() -> ReturnType
}
In practice, this abstraction lacks some definition, as we're missing some information about what we should pass to the algorithm. For the sake of our demonstration, let's rewrite the run() function with a single argument. As we're using generics, this argument can be anything we want:
protocol Strategy {
associatedtype ReturnType
associatedtype ArgumentType
func run(argument: ArgumentType) -> ReturnType
}
Now, for all the objects that need a different strategy, we can define a new Context type. This type will need to have an associated StrategyType, and keep a reference to the Strategy:
protocol Context {
associatedtype StrategyType: Strategy
var strategy: StrategyType { get set }
}
With the Context and Strategy protocols defined, we can jump into an example that showcases this design pattern.