Hubbry Logo
Strategy patternStrategy patternMain
Open search
Strategy pattern
Community hub
Strategy pattern
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Strategy pattern
Strategy pattern
from Wikipedia

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives runtime instructions as to which in a family of algorithms to use.[1]

Strategy lets the algorithm vary independently from clients that use it.[2] Strategy is one of the patterns included in the influential book Design Patterns by Gamma et al.[3] that popularized the concept of using design patterns to describe how to design flexible and reusable object-oriented software. Deferring the decision about which algorithm to use until runtime allows the calling code to be more flexible and reusable.

For instance, a class that performs validation on incoming data may use the strategy pattern to select a validation algorithm depending on the type of data, the source of the data, user choice, or other discriminating factors. These factors are not known until runtime and may require radically different validation to be performed. The validation algorithms (strategies), encapsulated separately from the validating object, may be used by other validating objects in different areas of the system (or even different systems) without code duplication.

Typically, the strategy pattern stores a reference to code in a data structure and retrieves it. This can be achieved by mechanisms such as the native function pointer, the first-class function, classes or class instances in object-oriented programming languages, or accessing the language implementation's internal storage of code via reflection.

Structure

[edit]

UML class and sequence diagram

[edit]
A sample UML class and sequence diagram for the Strategy design pattern.[4]

In the above UML class diagram, the Context class does not implement an algorithm directly. Instead, Context refers to the Strategy interface for performing an algorithm (strategy.algorithm()), which makes Context independent of how an algorithm is implemented. The Strategy1 and Strategy2 classes implement the Strategy interface, that is, implement (encapsulate) an algorithm.
The UML sequence diagram shows the runtime interactions: The Context object delegates an algorithm to different Strategy objects. First, Context calls algorithm() on a Strategy1 object, which performs the algorithm and returns the result to Context. Thereafter, Context changes its strategy and calls algorithm() on a Strategy2 object, which performs the algorithm and returns the result to Context.

Class diagram

[edit]
Strategy Pattern in UML

[5]

Strategy pattern in LePUS3 (legend)


Strategy and open–closed principle

[edit]
Accelerate and brake behaviors must be declared in each new car model.

According to the strategy pattern, the behaviors of a class should not be inherited. Instead, they should be encapsulated using interfaces. This is compatible with the open–closed principle (OCP), which proposes that classes should be open for extension but closed for modification.

As an example, consider a car class. Two possible functionalities for car are brake and accelerate. Since accelerate and brake behaviors change frequently between models, a common approach is to implement these behaviors in subclasses. This approach has significant drawbacks; accelerate and brake behaviors must be declared in each new car model. The work of managing these behaviors increases greatly as the number of models increases, and requires code to be duplicated across models. Additionally, it is not easy to determine the exact nature of the behavior for each model without investigating the code in each.

The strategy pattern uses composition instead of inheritance. In the strategy pattern, behaviors are defined as separate interfaces and specific classes that implement these interfaces. This allows better decoupling between the behavior and the class that uses the behavior. The behavior can be changed without breaking the classes that use it, and the classes can switch between behaviors by changing the specific implementation used without requiring any significant code changes. Behaviors can also be changed at runtime as well as at design-time. For instance, a car object's brake behavior can be changed from BrakeWithABS() to Brake() by changing the brakeBehavior member to:

Brake* brakeBehavior = new Brake();
package org.wikipedia.examples;

/* Encapsulated family of Algorithms
 * Interface and its implementations
 */
interface IBrakeBehavior {
    public void brake();
}

class BrakeWithABS implements IBrakeBehavior {
    public void brake() {
        System.out.println("Brake with ABS applied");
    }
}

class Brake implements IBrakeBehavior {
    public void brake() {
        System.out.println("Simple Brake applied");
    }
}

// Client that can use the algorithms above interchangeably
abstract class Car {
    private IBrakeBehavior brakeBehavior;

    public Car(IBrakeBehavior brakeBehavior) {
      this.brakeBehavior = brakeBehavior;
    }

    public void applyBrake() {
        brakeBehavior.brake();
    }

    public void setBrakeBehavior(IBrakeBehavior brakeType) {
        this.brakeBehavior = brakeType;
    }
}

// Client 1 uses one algorithm (Brake) in the constructor
class Sedan extends Car {
    public Sedan() {
        super(new Brake());
    }
}

// Client 2 uses another algorithm (BrakeWithABS) in the constructor
class SUV extends Car {
    public SUV() {
        super(new BrakeWithABS());
    }
}

// Using the Car example
public class CarExample {
    public static void main(String[] arguments) {
        Car sedanCar = new Sedan();
        sedanCar.applyBrake(); // This will invoke class "Brake"

        Car suvCar = new SUV();
        suvCar.applyBrake(); // This will invoke class "BrakeWithABS"

        // set brake behavior dynamically
        suvCar.setBrakeBehavior(new Brake());
        suvCar.applyBrake(); // This will invoke class "Brake"
    }
}

See also

[edit]

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
The Strategy pattern is a behavioral design pattern that defines a family of interchangeable , encapsulates each as a separate class implementing a , and allows clients to select and switch between them at runtime without altering the client code. This pattern enables to vary independently from the clients that use them, promoting flexibility in by avoiding hard-coded conditional logic for selection. Introduced in the 1994 book Design Patterns: Elements of Reusable Object-Oriented Software by , Richard Helm, Ralph Johnson, and John Vlissides—commonly known as the Gang of Four (GoF)—the Strategy pattern addresses common challenges in where multiple variants of an need to be supported dynamically. The pattern's core structure involves three main participants: a class that maintains a reference to a object and delegates algorithm execution to it; a interface or abstract class declaring the algorithm's method; and concrete classes that implement specific algorithms. This composition allows the Context to remain unchanged while swapping strategies, adhering to the open-closed principle by extending behavior through new strategy classes rather than modifying existing code. The Strategy pattern is applicable in scenarios involving families of related algorithms, such as sorting methods, options, or compression techniques, where the choice of depends on runtime conditions like size or user preferences. It is particularly useful for eliminating large switch statements or if-else chains that select behaviors, instead providing a more maintainable and extensible alternative. Key advantages include defining a clear of interchangeable algorithms, enabling reuse across contexts, and simplifying by isolating strategies; however, it can introduce complexity through additional classes and may not be ideal if strategies are few or simple enough for direct parameterization. The pattern relates to others like State (which also encapsulates varying behavior but focuses on object state changes) and Flyweight (sharing strategies to optimize memory), and it contrasts with Template Method by allowing full algorithm replacement rather than partial variation.

Definition and Intent

Definition

The Strategy pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one as an object, and makes them interchangeable within the context of a client, allowing the algorithm to vary independently from clients that use it. It consists of three key components: the Context, which is the client class that uses a ; the , an interface or abstract class that declares the algorithm to be implemented; and ConcreteStrategy, concrete classes that implement the interface to provide specific algorithm behaviors. The pattern was introduced in the 1994 book Design Patterns: Elements of Reusable Object-Oriented Software by , Richard Helm, Ralph Johnson, and John Vlissides, commonly known as the . As a behavioral design pattern, it focuses on the communication between objects, using and composition to manage algorithms and responsibilities.

Intent and Motivation

The intent of the Strategy pattern is to define a family of algorithms, encapsulate each one within its own class, and make them interchangeable at runtime, allowing the algorithm to vary independently from the clients that use it. This pattern is motivated by scenarios where a single class must perform different behaviors based on varying conditions, such as selecting among multiple algorithms depending on input data or environmental factors. Without Strategy, such flexibility often results in bloated code filled with conditional statements (e.g., numerous if-else branches to choose between sorting algorithms like for large datasets or for small ones), which violates the single responsibility principle and makes maintenance difficult. By delegating algorithm selection and execution to interchangeable strategy objects, the pattern separates the concerns of behavior choice from the core logic of the context class, reducing and enabling runtime swaps without client-side modifications. A practical example is a processing that must handle diverse methods like authorization or integration; embedding each method's logic directly in the processor class would lead to entangled code, whereas treating them as strategies allows seamless addition or substitution of options.

Structure

Class Diagram

The class diagram of the pattern depicts a static structure that supports the encapsulation and interchangeability of algorithms through and polymorphism. As outlined in the foundational work on , the diagram features a small set of interrelated classes without complex hierarchies. The primary components include the class, which represents the client that uses the algorithm; the abstract class or interface; and one or more ConcreteStrategy classes. The Context class contains a private reference to a Strategy object, typically named strategy, and includes a public method such as contextInterface() that delegates the core algorithmic work by invoking the Strategy's algorithmInterface() method. The Strategy component declares the abstract algorithmInterface() method, serving as the common contract for all algorithm variants without providing an implementation itself. Each ConcreteStrategy class, such as ConcreteStrategyA or ConcreteStrategyB, implements this method with a specific algorithm, enabling diverse behaviors like sorting via quicksort or mergesort. Key relationships in the diagram consist of a composition association (denoted by a filled diamond) from Context to Strategy, signifying that the Context owns and manages the lifecycle of its Strategy instance; and realization relationships (dashed arrows with hollow triangles) from each ConcreteStrategy to Strategy, indicating implementation of the abstract method. Notably, no inheritance exists between Context and Strategy, preserving their independence. This arrangement facilitates polymorphism, where the Context interacts solely with the Strategy interface, allowing seamless substitution of ConcreteStrategy instances at runtime without modifying the Context code. A frequent variation replaces the Strategy interface with an abstract class to accommodate shared helper methods or default implementations across ConcreteStrategies, such as common preprocessing steps, while still supporting multiple concrete realizations.

Sequence Diagram

The sequence diagram for the Strategy pattern depicts the runtime interactions that enable interchangeable algorithms, focusing on how the Client configures the with a specific strategy and how the Context delegates execution without knowledge of the implementation. As defined in the seminal work by Gamma et al., the diagram includes lifelines for the Client (which initiates the process), the (which maintains a reference to the interface), the abstract Strategy, and one or more instances (which provide the actual algorithmic behavior). The primary flow begins with the Client instantiating a ConcreteStrategy object and passing it to the Context via a setter method, such as setStrategy(). This establishes the polymorphic reference in the . Subsequently, the Client sends a request to the , prompting the to invoke the execute() or equivalent method on its Strategy reference. The call resolves dynamically to the ConcreteStrategy's , where the specific is performed, and the result is returned through the chain back to the Client. This interaction highlights runtime polymorphism and encapsulation of implementation details within the strategy objects. Key messages in the sequence include:
  • Client → ConcreteStrategy: new ConcreteStrategy() (instantiation)
  • Client → Context: setStrategy(ConcreteStrategy) (configuration)
  • Client → Context: request() (initiation)
  • Context → Strategy: execute() (delegation)
  • Strategy → self: performAlgorithm() (execution in concrete class, often shown as an activation bar on the ConcreteStrategy lifeline)
To illustrate switching strategies mid-execution, an extended sequence might show the Client sending another setStrategy(new DifferentConcreteStrategy) after an initial request, followed by a second request() that now delegates to the updated strategy, allowing runtime adaptability without modifying the Context. This is particularly evident in the book's motivational example of varying text justification algorithms, where the Context (e.g., a ) can switch between ConcreteStrategies like LeftJustify and CenterJustify based on formatting requirements.

Implementation

Pseudocode

The Strategy pattern is typically implemented through an abstract strategy interface, concrete strategy classes, a context class that delegates to the strategy, and client code that configures and uses the context. This pseudocode provides a language-agnostic outline of the pattern's structure, emphasizing the encapsulation of interchangeable algorithms.

Strategy Interface

The core of the pattern is an abstract Strategy component defining a common interface for all supported algorithms.

interface Strategy { algorithm(): result; }

interface Strategy { algorithm(): result; }

This interface declares a single abstract method algorithm() that concrete implementations must provide, allowing the context to invoke algorithms uniformly without knowing their specifics.

Concrete Strategies

Concrete strategy classes implement the Strategy interface with specific algorithm logic. For example, two variants might sort data in ascending or descending order.

class ConcreteStrategyA implements Strategy { algorithm(): result { // Specific logic for algorithm A, e.g., sort ascending return "sorted ascending"; } } class ConcreteStrategyB implements Strategy { algorithm(): result { // Specific logic for algorithm B, e.g., sort descending return "sorted descending"; } }

class ConcreteStrategyA implements Strategy { algorithm(): result { // Specific logic for algorithm A, e.g., sort ascending return "sorted ascending"; } } class ConcreteStrategyB implements Strategy { algorithm(): result { // Specific logic for algorithm B, e.g., sort descending return "sorted descending"; } }

Each concrete strategy encapsulates a distinct behavior, enabling runtime selection based on needs.

Context Class

The Context maintains a reference to a Strategy object and delegates algorithm execution to it, promoting flexibility in behavior.

class Context { private Strategy strategy; constructor(Strategy initialStrategy) { this.strategy = initialStrategy; } setStrategy(Strategy newStrategy) { this.strategy = newStrategy; } contextMethod(): result { if (this.strategy == null) { throw new Error("No strategy assigned"); } return this.strategy.algorithm(); } }

class Context { private Strategy strategy; constructor(Strategy initialStrategy) { this.strategy = initialStrategy; } setStrategy(Strategy newStrategy) { this.strategy = newStrategy; } contextMethod(): result { if (this.strategy == null) { throw new Error("No strategy assigned"); } return this.strategy.algorithm(); } }

The context uses a private field for the strategy, with a constructor or setter for assignment. The contextMethod() ensures the strategy is not null before delegation to prevent runtime errors, then invokes the algorithm.

Client Usage

A client instantiates strategies, assigns them to a , and invokes the 's method to execute the selected .

class Client { main() { strategyA = new ConcreteStrategyA(); [context](/page/Context) = new [Context](/page/Context)(strategyA); resultA = [context](/page/Context).contextMethod(); // Uses ascending sort strategyB = new ConcreteStrategyB(); [context](/page/Context).setStrategy(strategyB); resultB = [context](/page/Context).contextMethod(); // Switches to descending sort } }

class Client { main() { strategyA = new ConcreteStrategyA(); [context](/page/Context) = new [Context](/page/Context)(strategyA); resultA = [context](/page/Context).contextMethod(); // Uses ascending sort strategyB = new ConcreteStrategyB(); [context](/page/Context).setStrategy(strategyB); resultB = [context](/page/Context).contextMethod(); // Switches to descending sort } }

This usage demonstrates dynamic strategy interchangeability at runtime, allowing the same to support varying behaviors without modification.

Language-Specific Example

The is commonly implemented in , an object-oriented language well-suited for demonstrating behavioral through interfaces and polymorphism. The following example adapts the abstract structure to concrete , using simple arithmetic operations ( and ) to show how interchangeable strategies can be applied within a context class.

Strategy Interface

The Strategy interface defines the contract for all concrete strategies, specifying a single method to execute the algorithm with two integer parameters.

java

public interface Strategy { int execute(int a, int b); }

public interface Strategy { int execute(int a, int b); }

Concrete Strategies

Concrete strategy classes implement the Strategy interface, each encapsulating a specific operation. Addition Strategy:

java

public class ConcreteAdd implements Strategy { @Override public int execute(int a, int b) { return a + b; } }

public class ConcreteAdd implements Strategy { @Override public int execute(int a, int b) { return a + b; } }

Subtraction Strategy:

java

public class ConcreteSubtract implements Strategy { @Override public int execute(int a, int b) { return a - b; } }

public class ConcreteSubtract implements Strategy { @Override public int execute(int a, int b) { return a - b; } }

Context Class

The Context class maintains a reference to a Strategy object and provides methods to set the strategy and delegate execution to it.

java

public class Context { private Strategy strategy; public Context() { // Default strategy can be set here if needed } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public int executeStrategy(int a, int b) { if (strategy == null) { throw new IllegalStateException("No strategy set"); } return strategy.execute(a, b); } }

public class Context { private Strategy strategy; public Context() { // Default strategy can be set here if needed } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public int executeStrategy(int a, int b) { if (strategy == null) { throw new IllegalStateException("No strategy set"); } return strategy.execute(a, b); } }

Client Usage

In the client code, a Context instance is created, and different strategies are set and executed at runtime to demonstrate interchangeability. The output shows the results of applying addition and then subtraction to the same inputs.

java

public class Client { public static void main(String[] args) { [Context](/page/Context) context = new [Context](/page/Context)(); // Use [addition](/page/Addition) [strategy](/page/Strategy) [Strategy](/page/Strategy) addStrategy = new ConcreteAdd(); context.setStrategy(addStrategy); System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); // Output: 10 + 5 = 15 // Switch to subtraction [strategy](/page/Strategy) [Strategy](/page/Strategy) subtractStrategy = new ConcreteSubtract(); context.setStrategy(subtractStrategy); System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); // Output: 10 - 5 = 5 } }

public class Client { public static void main(String[] args) { [Context](/page/Context) context = new [Context](/page/Context)(); // Use [addition](/page/Addition) [strategy](/page/Strategy) [Strategy](/page/Strategy) addStrategy = new ConcreteAdd(); context.setStrategy(addStrategy); System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); // Output: 10 + 5 = 15 // Switch to subtraction [strategy](/page/Strategy) [Strategy](/page/Strategy) subtractStrategy = new ConcreteSubtract(); context.setStrategy(subtractStrategy); System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); // Output: 10 - 5 = 5 } }

This implementation leverages 's interface-based polymorphism, allowing the Context to remain agnostic to the specific strategy used, as it interacts solely through the Strategy reference. The code compiles with standard Java compilers (e.g., from JDK 8 or later) and runs without additional dependencies, producing the expected output when executed via java Client. Extending the example to include more operations, such as , would involve adding new classes implementing Strategy without modifying existing code.

Benefits and Trade-offs

Advantages

The Strategy pattern enhances flexibility in by enabling the dynamic selection and interchange of algorithms at runtime, without necessitating recompilation or modification of the client code that uses them. This decoupling allows developers to extend functionality by introducing new strategies without altering the core class, aligning with the open-closed principle by permitting extension while keeping the system open for modification only through new strategy implementations. By encapsulating each algorithm within its own concrete strategy class, the pattern promotes adherence to the Single Responsibility Principle, as each strategy focuses solely on implementing one specific behavior or , thereby isolating changes and reducing the risk of unintended side effects in the broader system. This separation ensures that modifications to a particular affect only its dedicated class, simplifying maintenance and evolution of the codebase. The pattern facilitates reusability by allowing individual strategy classes to be shared across multiple context classes or even different applications, promoting the creation of modular, interchangeable components that can be composed into various configurations without duplication. This approach leverages object-oriented composition to build families of related behaviors that can be reused independently of their usage context. Testability is improved through the isolation of strategies, enabling unit tests to target each algorithm independently without the need to instantiate or mock the entire context, which eliminates the complexity of testing conditional logic scattered across multiple if-else branches in monolithic implementations. This modular structure supports more focused and efficient testing practices, as each strategy can be verified in isolation for correctness and performance. For instance, in a application, the Strategy pattern allows seamless switching between routing algorithms—such as one prioritizing the fastest path versus another favoring scenic routes—by injecting the appropriate strategy into the context at runtime, thereby adapting to user preferences without refactoring the application's core logic.

Disadvantages

The Strategy pattern introduces additional classes, one for each distinct algorithm or behavior, which can increase the overall of the by expanding the number of components that must be managed and understood. This proliferation of classes often results in more objects being instantiated at runtime, potentially complicating and efforts. A key drawback is the added responsibility placed on the client code, which must be aware of the available strategies and capable of selecting the appropriate one based on context or requirements. This awareness can expose implementation details to the client, potentially leading to tighter or errors if the differences between strategies are not well-documented or intuitive. The pattern incurs a minor performance overhead stemming from dynamic dispatch mechanisms, such as virtual function calls, and the costs associated with creating and switching between strategy objects, which may be negligible in most applications but noticeable in performance-critical scenarios. Additionally, frequent communication between the context and strategy objects can introduce further overhead if not minimized through stateless designs. For cases involving only a handful of stable algorithms that rarely evolve, the Strategy pattern may overcomplicate the solution unnecessarily, as simpler approaches like conditional statements within the context class would suffice without the added abstraction layers. In resource-constrained environments, such as embedded systems, the increased number of strategy objects can exacerbate memory usage issues, making the pattern less suitable where efficiency is paramount over flexibility.

Relation to Other Concepts

Open-Closed Principle

The Open-Closed Principle (OCP), introduced by in 1988, states that software entities such as classes, modules, and functions should be open for extension but closed for modification. This principle promotes designs where new functionality can be added through extension mechanisms like or composition, without altering the source code of existing components, thereby reducing the risk of introducing bugs during modifications. The Strategy pattern aligns with the OCP by allowing the Context class to remain unchanged when new behaviors are introduced through additional ConcreteStrategy classes. Extension occurs by creating new strategy implementations that adhere to the Strategy interface, enabling the system to incorporate varied algorithms without modifying the core Context logic or existing strategies. This adherence to OCP is evident in scenarios where the pattern encapsulates interchangeable algorithms, such as sorting methods in a data processing library, where new sorting variants can be added independently. The mechanism supporting this alignment relies on polymorphism and : the delegates behavior to a object via an interface reference, permitting runtime substitution without internal changes to the or Client code. For instance, in an e-commerce order processing system, introducing a new strategy (e.g., ) involves only defining a new ConcreteStrategy class, leaving the OrderProcessor () unmodified. However, the Strategy pattern's support for OCP has limitations; while the Context and existing strategies remain closed to modification, Client code may require updates to instantiate and select the new strategies, potentially introducing minor modifications outside the core entities.

Comparison with State Pattern

The State pattern enables an object to alter its behavior when its internal state changes, making it appear as though the object has changed its class; it encapsulates state-specific behavior by delegating to state objects that can trigger transitions between states. In contrast, the pattern defines a family of interchangeable algorithms, encapsulating each in its own class and allowing a client to select and switch them at runtime without altering the object. The primary difference lies in and binding: focuses on external selection of independent algorithms, often bound once at initialization for pluggable behaviors, whereas State manages dynamic internal transitions where states may depend on and modify the context's state. Both patterns share structural similarities as behavioral designs from the catalog, relying on composition and polymorphism: a delegates varying behavior to interchangeable objects via a , promoting and adherence to the open-closed principle. However, treats algorithms as stateless and unaware of each other, avoiding interdependencies, while State introduces potential cycles or dependencies as states can invoke context methods to change the current state. Choose the Strategy pattern when the goal is to support multiple, interchangeable algorithms independent of the object's state, such as selecting different sorting methods in a data processor. Opt for the State pattern when behavior inherently depends on the object's lifecycle or internal conditions, like managing play, pause, and stop states in a media player where each state dictates valid transitions. For instance, a file compression tool might use Strategy to swap algorithms like ZIP or GZIP externally based on user preference, without internal state changes, whereas a TCP connection handler employs State to evolve through listening, established, and closed phases, with each phase restricting actions.

Relation to Flyweight Pattern

The is a structural that minimizes memory usage by sharing as much data as possible between similar objects. The Strategy pattern relates to Flyweight when strategies are stateless and can be shared among multiple contexts to optimize resource usage. In the Gang of Four's description, shared strategies act as flyweights if they do not maintain state across invocations. This combination is useful in scenarios with many contexts using the same algorithm, such as in graphics rendering where common rendering strategies are reused across numerous objects.

Comparison with Template Method Pattern

The Template Method pattern is a behavioral design pattern that defines the skeleton of an algorithm in a base class, allowing subclasses to override specific steps without changing the overall structure. In contrast to , which uses composition to allow full runtime replacement of algorithms, Template Method relies on inheritance for compile-time variation of algorithm parts. Strategy provides greater flexibility for switching entire behaviors dynamically, while Template Method ensures a fixed sequence with customizable hooks, making it suitable for frameworks where the algorithm outline is invariant but details vary.
Add your contribution
Related Hubs
User Avatar
No comments yet.