Strategy.
“black” of the business world — strategy will never go out of fashion.
By which, I mean the word “strategy”.
Such gravitas. Such authority.
As a classically-trained consultant, wrung through the gauntlet of 12 weeks of h̶e̶a̶v̶y̶ ̶d̶r̶i̶n̶k̶i̶n̶g̶
graduate training, I know all about strategy. I can even use strategy in my code.
Since I have some bandwidth, I’ll bring you up to speed.
Let’s touch base with the 30,000-foot view: the Strategy design pattern allows you to select the behaviour you want dynamically. In practice, this means you can define a family of algorithms, give them a shared interface, and make them interchangeable at runtime.
Sorting Strategies
Let’s not boil the ocean — here’s a command-line app I wrote in 2019 to demo Strategy using a low-hanging fruit: sorting algorithms.
To implement the Strategy pattern, we define our game-changing interface:
protocol SortingAlgorithm {
func sort(_ numbers: [Int]) -> [Int]
}
Then we create concrete implementations which synergise with said interface:
/// Time: O(nlogn)
/// Space: O(logn)
struct QuickSort: SortingAlgorithm {
func sort(_ numbers: [Int]) -> [Int] { ... }
}
/// Time: O(n^2)
/// Space: O(1)
struct BubbleSort: SortingAlgorithm {
func sort(_ numbers: [Int]) -> [Int] { ... }
}
/// Time: O(nlogn)
/// Space: O(n)
struct MergeSort: SortingAlgorithm {
func sort(_ numbers: [Int]) -> [Int] { ... }
}
Set a SortingAlgorithm
property and run its sort()
method to order your array. Modify the strategy
variable anytime for a sort-u-ational paradigm shift.
private var strategy: SortingAlgorithm?
self.strategy = MergeSort()
strategy?.sort([3, 1, 2]) // 1, 2, 3
Why is this useful?
Get those performance characteristics on your radar — in low-memory situations, think outside the box with O(1)
space complexity (and accept sluggish execution). Per the 80/20 rule, you’ll often just use the faster O(nlogn)
time complexity sorts. A win-win!
Switching Internal Implementations
Per my last email, I added a basic strategy to an authentication service:
enum AuthStrategy {
case naive
case ideal
}
actor AuthService {
var strategy: AuthStrategy = .ideal
func getAuthToken() async throws -> String {
switch strategy {
case .naive:
// simple but buggy
case .ideal:
// complex but optimal
}
}
}
This empowers us to initialise AuthService
with whichever internal implementation we like, and pivot on-the-fly.
The iOS SDK
You’ll start drinking from the firehose once you notice Strategies everywhere:
requestCachePolicy
onURLSession
.Animation curves like
.linear
,.easeIn
, and.spring
.AVCaptureDevice
for microphone and cameras.
We’ve completed our deep-dive into the Strategy pattern, I hope we’ve aligned and I’ve moved the needle on your understanding. Let’s take this offline.