Strategy is a behavioral design pattern that lets you define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.
One day you decided to create a navigation app for travelers. The app was centered around the beautiful-looking map, allowing users to orient quickly in any city. One of the most requested features of the app was automatic route planner, so you decided to devote it a special attention. Users would be able to enter a desired destination, tracking the fastest route on the screen.
While the first version of the app could only build routes over roads, it suited car travelers perfectly. But apparently, not everybody likes to drive on their vacation. So with the next update, you added an option to plan walking routes. Right after that, you added one more option, which allowed users to build routes relying on public transportation.
But that was only beginning. In the nearest future you planned to add route builder for cyclists. And later on, another option for building routes along sight seeings.
While from the business perspective the app was a success, the technical part caused you a lot of headaches.
Each time you added a new routing algorithm, the main
Map class had grown twice its size. At some point, the beast became too hard to maintain.
Any change to search algorithms, such as fixing a bug or slightly tuning the algorithm's behavior, affected the whole class, increasing the chance of making an error in already working code.
Finally, the teamwork became inefficient. Your team mates, which you hired right after the successful release, complain that they spend too much time resolving merge conflicts in the code of new features since they all related to just one large class.
The Strategy pattern suggests to take a class that does something important in a lot of different ways and extract all these algorithms into separate classes called strategies.
The original class, called context, will receive a field for storing a reference to one of the strategies. The context will delegate the work to a linked strategy instead of executing it on its own.
The context will not be responsible for picking the appropriate algorithm for the job. Instead, the client will pass a desired strategy to the context.
In fact, the context will not know much about strategies at all. It will communicate with all strategies through a basic interface, which will only expose the method for triggering an algorithm. This will make the context independent from strategies, allowing you to add new algorithms or modify existing without changing the code of the context or other strategies.
In our navigation app, each routing algorithm will be extracted to its own class with a single method
buildRoute. The method will accept origin and destination and return a collection of route checkpoints.
Even though each routing class might build a different route given the same arguments, the
Map class will not really care which one is selected, since its primary job is to render a set of checkpoints on the map.
Map will provide a method for switching active routing strategy, which will allow its clients, such as the buttons in the user interface, to alter current routing behavior.
You have to get to the airport. You can catch a bus, order a cab, or get on your bicycle. The way of transportation is a strategy. You pick a strategy depending on the context, such as the budget or time constraints.
Context stores a reference to a Concrete Strategy object, but works with it through a common Strategy interface. Context should expose a setter that allows other objects to replace linked strategy object.
Strategy declares the common interface for all strategies. This interface makes concrete strategies interchangeable in Context.
Concrete Strategies implement different algorithms, which aim to accomplish the same task in various ways.
The Context calls its strategy object each time when it needs to run that task. It does not know, however, in which way the task will be executed.
Clients know what strategy should be picked depending on the situations. They can configure the Context with a different strategy whenever they want at runtime, using the setter.
In this example, the Context uses strategies to execute various arithmetic operations.
When you have an object that should be able to do the same task in lots of different ways.
The Strategy pattern allows you to alter the object's behavior at runtime by supplying it with different sub-objects that actually perform the work.
When you have lots of similar classes, that differ with in the way the execute some behavior.
The Strategy pattern allows you to combine all these classes into one by extracting all variants of the behavior into a separate class hierarchy, making the behavior of original class customizable.
When you do not want to expose algorithm's implementation details to other classes.
The Strategy pattern isolates code, internal data, and dependencies of algorithms from other objects by extracting them into their own classes.
An algorithm to be executed is selected by a monstrous conditional operator. Each branch of the conditional represents different algorithm.
Strategy lets you decompose the conditional by extracting each algorithm into their own classes, all of which implement a common interface. Context delegates execution to one of these objects, instead of implementing the behavior by itself.
How to Implement
Identify an algorithm that the client would prefer to access through a "flex point."
Declare the common interface for all variations of the algorithm.
One-by-one, extract all algorithms into their own classes. They all should follow the common Strategy interface.
Add a field for storing a reference to the current strategy, as well as a setter to change it, into the Context class. The Context should work with this object only using the Strategy interface.
Context's clients must provide it with a proper strategy objects when they need the Context to perform the work in a certain way.
Pros and Cons
- Allows hot swapping algorithms at runtime.
- Isolates the code and data of the algorithms from the other classes.
- Replaces inheritance with delegation.
- Follows the Open/Closed Principle.
- Increases overall code complexity by creating multiple additional classes.
- Client must be aware of the differences between strategies to pick a proper one.
Relations with Other Patterns
State, Strategy, Bridge (and to some degree Adapter) have similar solution structures. They all share elements of the "handle/body" idiom. They differ in intent - that is, they solve different problems.
Command and Strategy are very similar, since both used to parametrize context with some action. Command can be used to convert any operation into an object. Operation's parameters become fields of that object. The conversion allows deferred or remote execution, storing command history, etc.
On ther other hand, the Strategy pattern usually describes different ways of doing the same thing. It helps to interchange these algorythms in a single context class.
Template Method uses inheritance to alter the algorithm by extending its parts in different classes. Strategy uses delegation to alter the object's behavior by replacing the nested strategy object. Template method works at the class level. Strategy allows you to change the behavior of individual objects.
State can be considered as an extension of the Strategy pattern. Both patterns use the composition to change the behavior of the main object by delegating the work to the helper objects. The Strategy pattern makes these objects completely independent. However, the State pattern allows state objects to alter the current state of the context with another state, making them interdependent.