Design Patterns: Strategy Pattern in JavaScript
• • 4 min readThere are 23 classical design patterns which was described in the original book, Design Patterns: Elements of Reusable Object-Oriented Software
. The patterns provides a solution to a particular problem which is repeated in the software development.
In this article, I'm going to describe the Strategy Pattern how it works, how and when should be apply. This pattern is known as Policy in other context.
Strategy Pattern: Basic Idea
The strategy pattern is a behavioral design pattern that enables selecting an algorithm at runtime — Wikipedia
Define a family of algorithms, encapsulate each one, and make them interchangeable.
Strategy lets the algorithm vary independently from clients that use it - Design Patterns: Elements of Reusable Object-Oriented Software
The main feature of this pattern is that the client have a set of algorithms in which a specific algorithm will be selected for use during runtime. This algorithms are interchangeable between them.
The following code show the classical problem in which you need selected a concrete algorithm in your app. In this code, you use the `switch` control structure of any programming language.
myMethod() {
switch (this._strategySelected) {
case SimpleStrategy:
ComposeWithSimpleStrategy();
break;
case TeXStrategy:
ComposeWithTeXStrategy();
break;
//...
}
// merge results with existing composition, if necessary
}
However, it can be more flexible using the Strategy Pattern which will be the following structure:
myMethod() {
this._strategySelected->Compose();
// merge results with existing composition, if necessary
}
The UML's diagram of this pattern is the following:
Each strategy is represented using a concrete object. So, the client/context contains a Strategy object (concreteStrategyA, concreteStrategyB,...) which implements the in interface Strategy. The key of interchange between strategy consists in implement a method in context which change the strategy, for example, setStrategy.
Strategy Pattern: When To Use
- The problem which resolve Strategy Pattern is when you need use several algorithms which have different variations. In that moment, you need create a concrete class to implement your algorithm (which can consists in a or some functions).
- Another interesting moment in which you detect that need this pattern is when there are conditional statements around a several algorithm which are related between them.
- Finally you must to use this pattern when most of your classes have related behaviours.
Strategy Pattern: Advantages
The Strategy Pattern have several advantages which can be summary in the following points:
- It's easy switching between different algorithms (strategies) in runtime because you're using polymorphism using the interfaces.
- Clean code because you avoid conditional-infested code (not complex).
- More clean code because you separate the concerns into classes (a class to each strategy).
Strategy pattern: A basic implementation using JavaScript
Now, I'm going to show you how you can implement this pattern using JavaScript, you must remember that Javascript lacks interfaces. So, you need programming a class called StrategyManager which is used as the interfaces:
class StrategyManager {
constructor() {
this._strategy = null;
}
set strategy(strategy) {
this._strategy = strategy;
}
get strategy() {
return this._strategy;
}
doAction() {
this._strategy.doAction();
}
}
This class contains a private attribute called _strategy which represents the strategy that will be used in this moment. The method doAction is the method which will be implement in each concrete Strategy. The Strategy pattern differ from the UML in JavaScript due to lack of OOP features in the language.
The implementation of each concrete Strategy is the following:
class Strategy1 {
doAction() {
console.log('Strategy1');
}
}
class Strategy2 {
doAction() {
console.log('Strategy2');
}
}
Note that the concrete method doAction is implemented in each concrete strategy.
Finally, the context/client must contains the StrategyManager (or strategy interface is the language is OO) to use the concrete strategy:
const strategyManager = new StrategyManager();
const strategy1 = new Strategy1();
const strategy2 = new Strategy2();
strategyManager.strategy = strategy1; //Assign Strategy1;
strategyManager.doAction();
strategyManager.strategy = strategy2; // Assign Strategy2;
strategyManager.doAction();
Strategy pattern: A set of strategies using JavaScript
In the following implementation, our StrategyManager can be more complex and contains a list of algorithms. In this case, you can change the attribute _strategy instead of an array called _strategies.
Finally, you can add new strategies in our list of strategies using the method addStrategy. The Strategy class have two attributes: 1) Strategy's name; 2) Algorithm (called handler). The method doAction is the used to invoke the concrete algorithm.
class StrategyManager {
constructor() {
this._strategies = [];
}
addStrategy(strategy) {
this._strategies = [...this._strategies, strategy];
}
getStrategy(name) {
return this._strategies.find(strategy => strategy._name === name);
}
}
class Strategy {
constructor(name, handler) {
this._name = name;
this._handler = handler;
}
doAction() {
this._handler();
}
}
Finally, the client/context code where we use the concrete strategy is the following:
// Client-Context
const strategyManager = new StrategyManager();
const strategy1 = new Strategy('strategy1', () => console.log('Strategy1'));
const strategy2 = new Strategy('strategy2', () => console.log('Strategy2'));
strategyManager.addStrategy(strategy1);
strategyManager.addStrategy(strategy2);
// Choose first strategy.
const strategyA = strategyManager.getStrategy('strategy1');
strategyA.doAction();
// Choose second strategy.
const strategyB = strategyManager.getStrategy('strategy2');
strategyB.doAction();
// Choose unsupported strategy.
const strategyC = strategyManager.getStrategy('strategy3');
try {
strategyC.doAction();
} catch (err) {
console.error('Caught Error');
console.error(err);
}
The first part is create concrete strategies (which can be construct using the Singleton pattern and the Factory pattern) and added in our strategyManager (which could be our interface). The next part of the client is selected the strategy to use, this strategy can be selected using a GUI or CLI from our app.
Finally, you can note that if a unsupported strategy is selected the system return an error. This can be used when you want provide a premium algorithm to your system.
Conclusion
Strategy Pattern is a pattern which can avoid complex in your code when need selected a concrete algorithm. In this post you can obtained a simple implementation using the language JavaScript which lacks interfaces. In the case that you use a programming language which has interface you can follow the pattern's UML.
The most important is not implement the pattern as I've shown you but you need know what's the problem which the pattern resolve and why you must use because the implementation will be different depends of the programming language.
More more more...
- Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, & Vlissides, Addison Wesley, 1995.
- The Strategy Pattern — Wikipedia.
- https://www.dofactory.com/javascript/strategy-design-pattern
- https://github.com/piecioshka/pattern-strategy
- https://github.com/sohamkamani/javascript-design-patterns-for-humans#-strategy
- https://blog.bitsrc.io/keep-it-simple-with-the-strategy-design-pattern-c36a14c985e9
- https://github.com/matthewmagee05/StrategyDesignPattern
- The GitHub branch of this post is https://github.com/Caballerog/blog/tree/master/strategy-pattern