Strategy Pattern

The strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This pattern lets the algorithm vary independently from clients that use it.

Here's a simple example of the strategy pattern:

  1. Strategy Trait: Defines a common interface for all supported algorithms.
  2. Concrete Strategies: Implement the Strategy trait for different algorithms.
  3. Context: Maintains a reference to a Strategy object and is configured with a Concrete Strategy object.
  4. Main Function: Creates instances of different strategies and the context, then executes the strategies within the context.
// Strategy type
pub trait Strategy {
    fn execute(&self, data: &str);
}

pub struct ConcreteStrategyA;

// Concrete Strategy A
impl Strategy for ConcreteStrategyA {
    fn execute(&self, data: &str) {
        println!("ConcreteStrategyA: {}", data);
    }
}

pub struct ConcreteStrategyB;

// Concrete Strategy B
impl Strategy for ConcreteStrategyB {
    fn execute(&self, data: &str) {
        println!("ConcreteStrategyB: {}", data);
    }
}

// Context type
pub struct Context {
    strategy: Box<dyn Strategy>,
}

// Concrete Context
impl Context {
    pub fn new(strategy: Box<dyn Strategy>) -> Self {
        Context { strategy }
    }

    pub fn set_strategy(&mut self, strategy: Box<dyn Strategy>) {
        self.strategy = strategy;
    }

    pub fn execute_strategy(&self, data: &str) {
        self.strategy.execute(data);
    }
}

fn main() {
    let strategy_a 
    = Box::new(ConcreteStrategyA);
    let strategy_b = Box::new(ConcreteStrategyB);

    let mut context = Context::new(strategy_a);
    context.execute_strategy("Hello, World!");

    context.set_strategy(strategy_b);
    context.execute_strategy("Hello, Rust!");
}

This pattern is useful for scenarios where you need to switch between different algorithms or behaviors at runtime.