Strategy design pattern | Behavioral pattern
Strategy design pattern
The Strategy design pattern is a behavioral pattern that defines a family of algorithms, encapsulates each algorithm, and makes them interchangeable. It allows a client to choose an algorithm from a family of algorithms dynamically. This pattern promotes the "composition over inheritance" principle by favoring composition to achieve flexibility and maintainability in code.
The key components of the Strategy design pattern include:
1. Context:
- Represents the client that utilizes the algorithms.
- Maintains a reference to the strategy interface.
- Allows clients to switch between different strategies dynamically.
2. Strategy Interface:
- Declares an interface common to all supported algorithms.
- Usually consists of a single method that encapsulates the algorithm.
3. Concrete Strategies:
- Implement the strategy interface with specific algorithmic implementations.
- Multiple concrete strategy classes can exist, each providing a different behavior.
4. Client:
- Initiates the context and sets its strategy.
- Utilizes the context to perform operations, relying on the currently set strategy.
These components work together to enable a flexible and interchangeable way of using different algorithms within a system without modifying the client code.
When to use:
1. Multiple Algorithms:
- Use the Strategy pattern when you have multiple algorithms for a task, and you want to make them interchangeable.
2. Dynamic Behavior:
- Employ it when you need to switch between different behaviors at runtime.
3. Avoiding Conditional Statements:
- Use it to eliminate conditional statements that select an algorithm based on some conditions.
4. Promoting Code Reusability:
- Apply it when you want to encapsulate each algorithm in a separate class, promoting code reuse.
Why to use:
1. Flexibility:
- The Strategy pattern provides a flexible way to define, switch, and extend algorithms without modifying the client code.
2. Maintainability:
- It helps in maintaining a clean and modular codebase by separating algorithms from the main logic.
3. Open/Closed Principle:
- It adheres to the Open/Closed Principle, allowing you to add new algorithms without modifying existing code.
4. Testability:
- Strategies can be tested independently, promoting easier unit testing of specific algorithm implementations.
Where to use:
1. Sorting Algorithms:
- Apply it in scenarios where different sorting algorithms (e.g., bubble sort, quicksort) can be used interchangeably.
2. Payment Processing:
- Use it for handling various payment methods as separate strategies, allowing easy integration of new payment methods.
3. Graphics Rendering:
- In graphics rendering, different rendering algorithms (e.g., rasterization, ray tracing) can be implemented as strategies.
4. Game Development:
- Apply the Strategy pattern when dealing with various enemy behaviors, allowing dynamic switching between different strategies for AI.
In summary, use the Strategy pattern when you anticipate changes in algorithms, want to avoid conditional statements, and aim for a modular and maintainable design. It is particularly useful in scenarios where different algorithms need to be applied dynamically.
Example of a payment system using the Strategy pattern in C#.
using System;
// Strategy Interface
public interface IPaymentStrategy
{
void ProcessPayment(double amount);
}
// Concrete Strategies
public class CreditCardPayment : IPaymentStrategy
{
public void ProcessPayment(double amount)
{
Console.WriteLine($"Processing credit card payment of ${amount}");
// Additional credit card processing logic
}
}
public class PayPalPayment : IPaymentStrategy
{
public void ProcessPayment(double amount)
{
Console.WriteLine($"Processing PayPal payment of ${amount}");
// Additional PayPal processing logic
}
}
// Context
public class ShoppingCart
{
private IPaymentStrategy paymentStrategy;
public void SetPaymentStrategy(IPaymentStrategy strategy)
{
paymentStrategy = strategy;
}
public void Checkout(double amount)
{
if (paymentStrategy == null)
{
Console.WriteLine("Please set a payment strategy first.");
return;
}
paymentStrategy.ProcessPayment(amount);
}
}
// Client
class Program
{
static void Main()
{
// Client code
var shoppingCart = new ShoppingCart();
// Using Credit Card payment strategy
shoppingCart.SetPaymentStrategy(new CreditCardPayment());
shoppingCart.Checkout(100.0);
// Using PayPal payment strategy
shoppingCart.SetPaymentStrategy(new PayPalPayment());
shoppingCart.Checkout(50.0);
}
}
In this example, we have a `ShoppingCart` class as the context, and it uses the `IPaymentStrategy` interface to delegate payment processing to concrete strategies (`CreditCardPayment` and `PayPalPayment`). The client code can dynamically set the payment strategy based on the user's choice.
Another real-world example of the Strategy pattern in a document editor application, where different file formats need to be exported.
using System;
// Strategy Interface
public interface IExportStrategy
{
void Export(string content);
}
// Concrete Strategies
public class PdfExport : IExportStrategy
{
public void Export(string content)
{
Console.WriteLine("Exporting content to PDF format:");
Console.WriteLine(content);
// Additional PDF export logic
}
}
public class WordExport : IExportStrategy
{
public void Export(string content)
{
Console.WriteLine("Exporting content to Word format:");
Console.WriteLine(content);
// Additional Word export logic
}
}
// Context
public class DocumentEditor
{
private IExportStrategy exportStrategy;
public void SetExportStrategy(IExportStrategy strategy)
{
exportStrategy = strategy;
}
public void ExportDocument(string content)
{
if (exportStrategy == null)
{
Console.WriteLine("Please set an export strategy first.");
return;
}
exportStrategy.Export(content);
}
}
// Client
class Program
{
static void Main()
{
// Client code
var documentEditor = new DocumentEditor();
// Using PDF export strategy
documentEditor.SetExportStrategy(new PdfExport());
documentEditor.ExportDocument("Sample content for PDF export.");
// Using Word export strategy
documentEditor.SetExportStrategy(new WordExport());
documentEditor.ExportDocument("Sample content for Word export.");
}
}
In this example, the `DocumentEditor` class represents the context, and it uses the `IExportStrategy` interface for exporting documents. Concrete strategies (`PdfExport` and `WordExport`) implement this interface, providing specific export logic for their respective formats. The client code can dynamically set the export strategy based on the desired output format.
Comments
Post a Comment