Strategy Pattern

The strategy pattern, also known as the policy pattern, defines a series of algorithms, encapsulates each algorithm, and allows them to be interchangeable. The strategy pattern allows algorithms to vary independently from clients that use them. This type of design pattern belongs to the behavioral pattern category.

Description

Completing a task can often be achieved in multiple ways, each way is known as a strategy. Depending on the different environment or conditions, we can choose different strategies to accomplish the task at hand. In software development, we often encounter similar situations where there are multiple approaches to implementing a certain functionality. In such cases, a design pattern can be used to make the system flexibly choose the approach for solving the problem, and also make it easy to add new approaches. In a software system, there may be many algorithms that can implement a certain functionality, such as searching or sorting. One common approach is hard coding these algorithms in a class. For instance, if multiple search algorithms need to be provided, these algorithms can be written in a class, with each method corresponding to a specific search algorithm. Another approach is to encapsulate these search algorithms in a unified method, using conditional statements such as if-else to make the choice. Both of these approaches can be referred to as hard coding. If a new search algorithm needs to be added, the source code of the encapsulated algorithm class needs to be modified, and the client calling code also needs to be modified. The class encapsulating the algorithms will become complex and difficult to maintain. Besides providing a dedicated search algorithm class, the algorithm code can also be directly included in the client program, which is even less advisable as it would lead to a large and hard-to-maintain client program, especially when there are numerous available algorithms to choose from. To address these issues, independent classes can be defined to encapsulate different algorithms, with each class encapsulating a specific algorithm. Here, each class encapsulating the algorithm can be referred to as a strategy. To ensure the consistency of these strategies, an abstract strategy class is generally used to define algorithms, with each specific algorithm corresponding to a specific concrete strategy class. While the state pattern changes the State objects combined by the Context through state transitions, the strategy pattern changes the combined Strategy objects through the Context's own decision-making.

Advantages

  • The strategy pattern provides perfect support for the open-closed principle, allowing users to choose algorithms or behaviors without modifying the original system and enabling the flexible addition of new algorithms or behaviors.
  • The strategy pattern provides a way to manage related algorithm families.
  • The strategy pattern provides a way to replace inheritance relationships.
  • Using the strategy pattern can avoid the use of multiple conditional statements.

Disadvantages

  • The client must be aware of all strategy classes and decide which one to use.
  • The strategy pattern will result in the creation of many strategy classes, which can be mitigated to some extent by using the flyweight pattern to reduce the number of objects.

Applicability

  • If a system has many classes whose only difference lies in their behavior, the strategy pattern can dynamically allow an object to choose one behavior from many behaviors.
  • A system needs to dynamically choose one algorithm from several.
  • If an object has many behaviors, without using the appropriate pattern, these behaviors would have to be implemented using multiple conditional selection statements.
  • If the client should not be aware of complex, algorithm-related data structures, encapsulating the algorithm and related data structures in specific strategy classes can enhance the confidentiality and security of the algorithm.

Implementation

class Strategy{
    operation(num1, num2){
        throw new Error("Abstract method cannot be called");
    }
}

class Add extends Strategy{
    operation(num1, num2){
        return num1 + num2;
    }
}

class Subtract extends Strategy{
    operation(num1, num2){
        return num1 - num2;
    }
}

class Multiply extends Strategy{
    operation(num1, num2){
        return num1 * num2;
    }
}

class Context {
 
   constructor(strategy){
      this.strategy = strategy;
   }
 
   execute(num1, num2){
      return this.strategy.operation(num1, num2);
   }
}

(function(){
    var context = new Context(new Add());
    console.log(context.execute(1, 2)); // 3
    var context = new Context(new Subtract());
    console.log(context.execute(1, 2)); // -1
    var context = new Context(new Multiply());
    console.log(context.execute(1, 2)); // 2
})();

Question of the Day

https://github.com/WindrunnerMax/EveryDay

Reference

https://www.runoob.com/w3cnote/state-vs-strategy.html https://www.runoob.com/design-pattern/strategy-pattern.html https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/strategy.html