The Strategy design pattern is a behavioral design pattern that allows you to define a family of algorithms, encapsulate each one as an object, and make them interchangeable at runtime. 

This pattern provides a way to delegate the algorithm implementation to different objects that implement the same interface.

A brief overview of the pattern and how it works:

  • Define the Strategy Interface: First, you need to define an interface or abstract class that represents the strategy. This interface should declare a method that will be implemented by all the concrete strategies.
  • Implement the Concrete Strategies: Next, you need to implement one or more concrete classes that implement the strategy interface. Each concrete class represents a specific algorithm or strategy.
  • Use the Strategy Objects: Finally, you need to use the concrete strategy objects in your code. You can do this by creating a context object that accepts a strategy object and delegates the work to it.

The Strategy pattern is useful when you have multiple algorithms or strategies that can be used interchangeably. It allows you to choose the appropriate strategy at runtime based on the context of the application.

 

Some benefits of using the Strategy pattern include:

 

  • Increased flexibility: Since the strategies are interchangeable, you can easily swap them out at runtime without affecting the rest of the code.
  • Improved maintainability: The strategy pattern helps you to keep your code clean and organized by separating the algorithms from the main logic of your application.
  • Enhanced testability: With the strategy pattern, it's easy to test each strategy independently, which can make it easier to identify and fix bugs.

The Strategy pattern consists of three main components:

 

  • Context: This is the object that requires a particular behavior or algorithm to be executed. 
  • Strategy: This is the interface that defines the behavior or algorithm to be executed. 
  • Concrete Strategies: These are the concrete classes that implement the Strategy interface. Each concrete strategy provides its own implementation.

The following UML diagram illustrates the relationships between these components:
 

Pattern1

 

In this diagram, the Context object contains a reference to the Strategy object, which is initially set to a default concrete strategy. The Context object can change the strategy at runtime by setting a different concrete strategy.

The Strategy interface defines a single method, execute(), which is implemented by each concrete strategy class.

The Context object invokes the execute() method on the current strategy, which results in the behavior or algorithm being executed.

 

In the following we will provide examples of how to implement Strategy design pattern:


Example1: Strategy pattern can be used in a payment application as the following:
Consider a payment system that supports multiple payment methods, such as credit cards, PayPal, and bank transfers. Each payment method has its own way of processing payments and handling errors.
To implement this functionality using the Strategy pattern, we could define a Context object called Payment that represents a payment transaction. The Payment class would contain a reference to a PaymentStrategy object that defines the payment method to be used and how the payment should be processed.


// Strategy interface
public interface IPaymentStrategy
{
    void ProcessPayment(decimal amount);
}

// Concrete strategy classes
public class CreditCardPaymentStrategy : IPaymentStrategy
{
    private string _cardNumber;
    private string _expirationDate;
    private string _securityCode;

    public CreditCardPaymentStrategy(string cardNumber, string expirationDate, string securityCode)
    {
        _cardNumber = cardNumber;
        _expirationDate = expirationDate;
        _securityCode = securityCode;
    }

    public void ProcessPayment(decimal amount)
    {
        // Process credit card payment business logic...
    }
}

public class PayPalPaymentStrategy : IPaymentStrategy
{
    private string _email;
    private string _password;

    public PayPalPaymentStrategy(string email, string password)
    {
        _email = email;
        _password = password;
    }

    public void ProcessPayment(decimal amount)
    {
        // Process PayPal payment business logic ...
    }
}

public class BankTransferPaymentStrategy : IPaymentStrategy
{
    private string _bankName;
    private string _accountNumber;
    private string _routingNumber;

    public BankTransferPaymentStrategy(string bankName, string accountNumber, string routingNumber)
    {
        _bankName = bankName;
        _accountNumber = accountNumber;
        _routingNumber = routingNumber;
    }

    public void ProcessPayment(decimal amount)
    {
        // Process bank transfer payment business logic...
    }
}

// Context class
public class Payment
{
    private decimal _amount;
    private IPaymentStrategy _paymentStrategy;

    public Payment(decimal amount, IPaymentStrategy paymentStrategy)
    {
        _amount = amount;
        _paymentStrategy = paymentStrategy;
    }

    public void ProcessPayment()
    {
        _paymentStrategy.ProcessPayment(_amount);
    }

    // Other methods for handling payment errors, verifying payment status, etc...
}

In this example, we have defined an IPaymentStrategy interface that defines the ProcessPayment() method. We have also defined three concrete classes, CreditCardPaymentStrategy, PayPalPaymentStrategy, and BankTransferPaymentStrategy, each implementing the IPaymentStrategy interface with its own implementation of ProcessPayment().

The Payment class contains a reference to an IPaymentStrategy object that can be set to one of the concrete PaymentStrategy implementations. The ProcessPayment() method invokes the ProcessPayment() method on the current IPaymentStrategy object, which processes the payment using the corresponding payment method.

To use this implementation, we can create Payment objects and set their IPaymentStrategy objects as needed:

Payment creditCardPayment = new Payment(100.00m, new CreditCardPaymentStrategy("1234567890123456", "12/25", "123"));
creditCardPayment.ProcessPayment();

Payment payPalPayment = new Payment(50.00m, new PayPalPaymentStrategy("john.doe@example.com", "password123"));
payPalPayment.ProcessPayment();

Payment bankTransferPayment = new Payment(200.00m, new BankTransferPaymentStrategy("Bank of America", "1234567890", "012345678"));
bankTransferPayment.ProcessPayment();


Example2: Strategy pattern can be used in a game application:
Consider a game that allows players to choose from different character classes, such as warrior, mage, and rogue. Each character class has its own set of abilities, such as attacking with a sword, casting spells, or using stealth.

To implement this functionality using the Strategy pattern, we could define a Context object called Character that represents a player character. The Character class would contain a reference to an AbilityStrategy object that defines the set of abilities available to the character.

// Strategy interface
public interface IAbilityStrategy
{
    void UseAbility();
}

// Concrete strategy classes
public class SwordAttackAbility : IAbilityStrategy
{
    public void UseAbility()
    {
        // Perform sword attack...
    }
}

public class SpellCastAbility : IAbilityStrategy
{
    public void UseAbility()
    {
        // Cast a spell...
    }
}

public class StealthAbility : IAbilityStrategy
{
    public void UseAbility()
    {
        // Use stealth mode...
    }
}


// Context class
public class Character
{
    private string _name;
    private IAbilityStrategy _abilityStrategy;

    public Character(string name, IAbilityStrategy abilityStrategy)
    {
        _name = name;
        _abilityStrategy = abilityStrategy;
    }

    public void UseAbility()
    {
        _abilityStrategy.UseAbility();
    }

    // Other methods for moving the character, interacting with objects, etc...
}

In this example, we have defined an IAbilityStrategy interface that defines the UseAbility() method. We have also defined three concrete classes, SwordAttackAbility, SpellCastAbility, and StealthAbility, each implementing the IAbilityStrategy interface with its own implementation of UseAbility().

The Character class contains a reference to an IAbilityStrategy object that can be set to one of the concrete AbilityStrategy implementations. The UseAbility() method invokes the UseAbility() method on the current IAbilityStrategy object, which performs the corresponding ability.

To use this implementation, we can create Character objects and set their IAbilityStrategy objects as needed:

Character warrior = new Character("Grommash", new SwordAttackAbility());
warrior.UseAbility(); // Performs a sword attack

Character mage = new Character("Jaina", new SpellCastAbility());
mage.UseAbility(); // Casts a spell

Character rogue = new Character("Garona", new StealthAbility());
rogue.UseAbility(); // Uses stealth mode

 

Overall, the Strategy pattern is a powerful pattern for organizing and managing complex algorithms in your code. By encapsulating each algorithm as a strategy object, you can create a flexible and maintainable architecture that can adapt to changing requirements over time.
 

Add new comment

This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.