Ghostboard pixel

The Strategy Pattern

The Strategy Pattern

The strategy pattern gives you the possibility to change the concrete implementation of an algorithm any time without breaking the rest of your code. Furthermore, it is a very good example of using polymorphism.

Hint: This post has been updated to Swift 3

The Idea

Imagine you want to use an algorithm, that solves a concrete problem, but you do not want do be dependent on a concrete implementation of this algorithm. A reason for that could be that you know that there will be a much more efficient algorithm in the future. Or, you want to be able to choose from algorithms with different properties. Sometimes you want for example a quicker implementation and at another time a more precise one.

To be able to change the implementation of the algorithm without breaking your whole code, there is the so called strategy pattern. First, you need to declare a protocol (in other programming languages called interface) for the algorithm. Then you instantiate an object of a class, that implements that protocol. However, this instantiation is of the protocol type, so that in the following it doesn’t matter which concrete implementation you are using:

Strategy

Example

Imagine that you have a class called ArrayStatistics, that calculates some statistical values for an integer array. For that, the class needs a sorting algorithm. For the class to be able to use different implementations of the sorting algorithm, we define the ArraySort protocol. Furthermore, we develop two concrete implementations of the sorting algorithm: SimpleSort and BubbleSort. Now we are able to set the actual implementation of the algorithm from outside of the class ArrayStatistics:

protocol ArraySort {
    func sortArray(intArray:[Int]) -> [Int]
}

class SimpleSort:ArraySort {
    func sortArray(intArray: [Int]) -> [Int] {
        var sortedArray = intArray
        var k = 0
        
        for i in stride(from: sortedArray.count-1, to: 0, by: -1) {
            for j in 0...(i-1) {
                if sortedArray[j] >= sortedArray[i] {
                    k = sortedArray[i]
                    sortedArray[i] = sortedArray[j]
                    sortedArray[j] = k
                }
            }
        }
        return sortedArray
    }
}

class BubbleSort:ArraySort {
    private var sortedArray: [Int] = []
    
    func sortArray(intArray: [Int]) -> [Int] {
        sortedArray = intArray
        return sort()
        
    }
    
    private func sort() -> [Int] {
        var k = 0
        for i in 0..<sortedArray.count-1 {
            if sortedArray[i] < sortedArray[i+1] {
                continue
            }
            k = sortedArray[i]
            sortedArray[i] = sortedArray[i+1]
            sortedArray[i+1] = k
            sort()
        }
        return sortedArray
    }
}

class ArrayStatistics {
    
    private var sortedArray: [Int]
    
    init(intArray:[Int],sorter:ArraySort) {
        self.sortedArray = sorter.sortArray(intArray: intArray)
    }
    
    func calculateSmallestNumber() -> Int {
        return sortedArray[0]
    }
    
    func calculateBiggestNumber() -> Int {
        return sortedArray.last!
    }
    
    func calculateMedian() -> Double {
        if sortedArray.count%2 == 0 {
            return (Double(sortedArray[(sortedArray.count/2)] + sortedArray[(sortedArray.count/2)-1]) / 2)
        }
        return Double(sortedArray[sortedArray.count/2])
    }
}

let arrayStatistics = ArrayStatistics(intArray: [3,1,10,185,37,4,6], sorter: SimpleSort())
print("\(arrayStatistics.calculateBiggestNumber())")
print("\(arrayStatistics.calculateSmallestNumber())")
print("\(arrayStatistics.calculateMedian())")

That means that the sorting algorithm of ArrayStatics can be changed without touching the code inside of ArrayStatistics! We just need to create an instance of the other sorting class:

let arrayStatistics = ArrayStatistics(intArray: [3,1,10,185,37,4,6], sorter: BubbleSort())
print("\(arrayStatistics.calculateBiggestNumber())")
print("\(arrayStatistics.calculateSmallestNumber())")
print("\(arrayStatistics.calculateMedian())")

[thrive_text_block color=”blue” headline=”Conclusion”]The strategy pattern is very effective for writing code, that depends not on a concrete implementation of an algorithm.[/thrive_text_block]

References

Image: @ solarseven / Shutterstock.com