Strategy pattern is also called Policy pattern
Strategy Pattern Characteristics
- Context: has a reference to a strategy and invokes it
- Calls IStrategy.Method(object);
- IStrategy: Defines the interface for the given strategy
- Defines the contract Method(object)
- Strategy: A concrete implementation of the strategy
- Implementation of Method(object)
Select an implementation at runtime based on user input without having to extend the class.
Example: ISalesTaxStrategy is an interface. We have multiple different implementations of Strategies to calculate tax. They all implement the ISalesTaxStrategy interface.
The code below doesn’t need to know what Strategy is chosen at this step. It only needs to invoke the GetTaxFor() Method.
1 | public ISalesTaxStrategy SalesTaxStrategy { get; set; } |
What did we achieve?
- A more extensible, object oriented and dynamic implementation
- Easily add new strategies without affecting existing ones
- Cleaner approach with single responsiblity in mind
Another thing we could do is to pass the interface to the GetTax() method.
1 | public decimal GetTax(ISalesTaxStrategy salesTaxStrategy) { |
And the concrete implementation of the strategy could be determined when we invoke the GetTax() Method
1 | order.GetTax(new SwedenSalesTaxStrategy() |
This is still meaning we have a hard dependency between the Order and the SalesTaxStrategy
Strategy Pattern with Dependency Injection
Pass the already created SalesTaxStrategy to the Order Contructor will help us remove the hard dependency between the Order and the Strategy.
1 | private ISalesTaxStrategy _salesTaxStrategy; |
Then Order(Context in Strategy Pattern) just need to invoke Strategy implementations without having to know which imeplementation it is invoking.
1 | public decimal GetTax() |
On Application start we create different Strategies based on user input
1 | switch (origin) |
Summary
- One of the most commonly used patterns
- Decouple the context and the concrete implementation
- Allows for a cleaner implementation in the context
- Easily extend with additional startegies without affecting current implementations
- Makes testing a lot easier as you can write mocked implementations to inject
- Identify existing implementations and where you have used the pattern before