Photo by Nina Mercado on Unsplash
Strategy Pattern - Design Patterns
Learn how the Strategy Pattern helps swap between algorithms in software design.
Category | Value |
Family | Behavioral |
Key Ideas | Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. |
Definition | The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it. |
OOP Concepts | Encapsulation, Inheritance, Polymorphism |
Introduction
Design patterns are essential tools in software development that provide proven solutions to common problems. The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm's behavior at runtime. This pattern promotes flexibility and reusability by allowing the algorithm to vary independently from the clients that use it.
What is a Strategy Pattern?
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
What does "a family of algorithms" and "encapsulate each one" mean?
A family of algorithms is a set of algorithms that do the same job but in different ways. For example, sorting algorithms like Bubble Sort, Quick Sort, Merge Sort, etc. are a family of algorithms that sort a list of elements but in different ways. Similarly in the SimUDuck example, the Duck family has different flying behaviors like FlyWithWings, FlyNoWay, FlyRocketPowered, etc.
Encapsulating each algorithm means that each algorithm is implemented in a separate class. This way, the algorithm can be changed without affecting the client code that uses the algorithm. For example, in the SimUDuck example, the flying behavior is encapsulated in separate classes like FlyWithWings, FlyNoWay, FlyRocketPowered, etc, and can be changed independently of the Duck class.
What does making algorithms interchangeable mean?
Making algorithms interchangeable means that the client code can switch between different algorithms at runtime. For example, in the SimUDuck example, the flying behavior of a Duck can be changed at runtime by setting the flying behavior to FlyWithWings, FlyNoWay, FlyRocketPowered, etc.
This is achieved by defining a common interface for all algorithms and implementing different algorithms as concrete classes that implement this interface. The client code can then use this interface to interact with different algorithms without knowing their concrete classes.
Deep Dive into the Strategy Pattern
Core Concepts
Context: The context is the class that uses a Strategy. It is composed of a reference to a Strategy object and delegates the algorithm's execution to the Strategy object. In the SimUDuck example, the
Duck
class acts as the context.Strategy Interface: This defines a common interface for all supported algorithms. The context uses this interface to call the algorithm defined by a concrete strategy. In the SimUDuck example,
IFlyBehavior
is the strategy interface.Concrete Strategies: These are classes that implement the Strategy interface. Each concrete strategy implements a specific algorithm. Examples include
FlyWithWings
,FlyNoWay
, andFlyRocketPowered
.
Detailed Example
The Strategy Pattern works by defining a common interface for all algorithms and implementing different algorithms as concrete classes that implement this interface. The client code interacts with these algorithms through the common interface, which allows it to switch between different algorithms at runtime.
Here's how the Strategy Pattern works in the SimUDuck example:
- Strategy Interface: Define a common interface for all flying behaviors:
public interface IFlyBehavior
{
void Fly();
}
- Concrete Strategies: Implement different flying behaviors as concrete classes that implement the
IFlyBehavior
interface:
public class FlyWithWings : IFlyBehavior
{
public void Fly()
{
Console.WriteLine("I'm flying with wings!");
}
}
public class FlyNoWay : IFlyBehavior
{
public void Fly()
{
Console.WriteLine("I can't fly!");
}
}
public class FlyRocketPowered : IFlyBehavior
{
public void Fly()
{
Console.WriteLine("I'm flying with a rocket!");
}
}
- Context Class: In the
Duck
class, add a property of typeIFlyBehavior
to hold the flying behavior and a method to set the flying behavior:
public abstract class Duck
{
protected IFlyBehavior flyBehavior;
public void SetFlyBehavior(IFlyBehavior flyBehavior)
{
this.flyBehavior = flyBehavior;
}
public void PerformFly()
{
flyBehavior.Fly();
}
}
- Setting Behavior at Runtime: Create different types of Ducks and set their flying behavior at runtime:
Duck mallard = new MallardDuck();
mallard.SetFlyBehavior(new FlyWithWings());
mallard.PerformFly(); // Output: I'm flying with wings!
Duck rubberDuck = new RubberDuck();
rubberDuck.SetFlyBehavior(new FlyNoWay());
rubberDuck.PerformFly(); // Output: I can't fly!
Duck decoyDuck = new DecoyDuck();
decoyDuck.SetFlyBehavior(new FlyRocketPowered());
decoyDuck.PerformFly(); // Output: I'm flying with a rocket!
What are the benefits of using a Strategy Pattern?
The Strategy Pattern offers the following benefits:
Open/Closed Principle: New algorithms can be added without modifying existing code.
Single Responsibility Principle: Each class has a single responsibility, making the code easier to understand and maintain.
Flexibility: It allows the client code to switch between different algorithms at runtime without changing its structure.
Reusability: It promotes code reuse by allowing different clients to reuse the same algorithms.
Testability: It makes it easier to test the algorithms independently of the client code.
Simplicity: It simplifies the client code by removing conditional statements and moving the algorithm selection logic to separate classes.
When to use Strategy Pattern?
You should use the Strategy Pattern when:
You have a family of algorithms that do the same job but in different ways.
You want to encapsulate each algorithm in a separate class.
You want to make the algorithms interchangeable at runtime.
You want to avoid conditional statements in the client code.
You want to reuse the algorithms in different contexts.
Real-World Applications
Payment Processing Systems: Different payment methods (credit card, PayPal, bank transfer) can be implemented as strategies.
Sorting Algorithms: Different sorting strategies (quick sort, merge sort, bubble sort) can be selected based on the context.
Compression Algorithms: Different compression strategies (zip, gzip, bzip2) can be used based on the file type and size.
Summary
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the client code to switch between different algorithms at runtime without changing its structure. The Strategy Pattern promotes flexibility, encapsulation, reusability, testability, and simplicity in the code. You should use the Strategy Pattern when you have a family of algorithms that do the same job but in different ways and you want to make them interchangeable at runtime.