???? Weekly Snippets
Every Sunday I write an email newsletter with some thoughts, life lessons and links to articles / books I enjoyed that week. I’d love for you to join.
Design Pattern - Behavioral: Strategy
Hey friends, as I promised on Instagram I am sharing with you my notes about coding and design pattern.
???? Definition
Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.
Strategy is a behavioral design pattern that turns a set of behaviors into objects and makes them interchangeable inside original context object.
The original object, called context, holds a reference to a strategy object and delegates it executing the behavior. In order to change the way the context performs its work, other objects may replace the currently linked strategy object with another one. This example shows a simple implementation of a list controller that is able to display models from different data sources:(MemoryStorage, CoreDataStorage, RealmStorage)
???????? Usage of the pattern
⚙️ Usage examples: The Strategy pattern is very common in Swift code. It’s often used in various frameworks to provide users a way to change the behavior of a class without extending it.
⚙️ Identification: Strategy patter can be recognized by a method that lets nested object do the actual work, as well as the setter that allows replacing that object with different one.
The Context defines the interface of interest to clients.
class Context {
/// The Context maintains a reference to one of the Strategy objects. The
/// Context does not know the concrete class of a strategy. It should work
/// with all strategies via the Strategy interface.
private var strategy: Strategy
/// Usually, the Context accepts a strategy through the constructor, but
/// also provides a setter to change it at runtime.
init(strategy: Strategy) {
self.strategy = strategy
}
/// Usually, the Context allows replacing a Strategy object at runtime.
func update(strategy: Strategy) {
self.strategy = strategy
}
/// The Context delegates some work to the Strategy object instead of
/// implementing multiple versions of the algorithm on its own.
func doSomeBusinessLogic() {
print("Context: Sorting data using the strategy (not sure how it'll do it)\\n")
let result = strategy.doAlgorithm(["a", "b", "c", "d", "e"])
print(result.joined(separator: ","))
}
}
The Strategy interface declares operations common to all supported version of the algorithm.
The Context uses this interface to all the algorithm defined by concrete Strategies.
protocol Strategy {
func doAlgorithm<T: Comparable>(_ data: [T]) -> [T]
}
Concrete Strategies implement the algorithm while following the base Strategy interface. The interface makes them interechangeable in the Context.
class ConcreteStrategyA: Strategy {
func doAlgorithm<T: Comparable>(_ data: [T]) -> [T] {
return data.sorted()
}
}
class ConcreteStrategyB: Strategy {
func doAlgorithm<T: Comparable>(_ data: [T]) -> [T] {
return data.sorted(by: >)
}
}
Let’s see how it all works togheter.
class StrategyConceptual: XCTestCase {
func test() {
/// The client code picks a concrete strategy and passes it to the
/// context. The client should be aware of the differences between
/// strategies in order to make the right choice.
let context = Context(strategy: ConcreteStrategyA())
print("Client: Strategy is set to normal sorting.\\n")
context.doSomeBusinessLogic()
print("\\nClient: Strategy is set to reverse sorting.\\n")
context.update(strategy: ConcreteStrategyB())
context.doSomeBusinessLogic()
}
}
Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e
Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
???? Weekly Snippets
Every Sunday I write an email newsletter with some thoughts, life lessons and links to articles / books I enjoyed that week. I’d love for you to join.