Strategy pattern

It's a behavioral pattern which turns possible changes in behavior accordingly to the chosen strategy.

Structure

Context

The use of context is recommended so that the client does not need to know the implementation details of each strategy. It can facilitate in using a default strategy and one can change strategies through it at runtime.

Pros

Reduces the use of conditionals

A more dynamic change in behavior

Cons

Clients need to know what strategy to use

Implementation

use std::any::Any;
use std::collections::HashMap;
use std::iter::Map;
use maplit::hashmap;

fn main() {
    let mut m = MarshallerContext::new(Box::new(JsonMarshaller{}));
    let example_map: HashMap<&str, &str> = hashmap! {
        "one" => "1",
        "two" => "2",
    };

    let json_output = m.marshall(&example_map);
    println!("json output:\n {}", json_output);

    m.strategy = Box::new(XmlMarshaller{});
    let xml_output = m.marshall(&example_map);
    println!("xml output:\n {}", xml_output);
}

pub trait Marshaller {
    fn marshall(&self, response: &HashMap<&str, &str>) -> String;
}

struct JsonMarshaller {
}

//A very simple example without recursive attributes
impl Marshaller for JsonMarshaller {
    fn marshall(&self, response: &HashMap<&str, &str>) -> String {
        return response.iter().fold(String::new(), |mut acc, (k, v)| {
            acc.push_str(&format!("{}: {}\n", k, v));
            acc
        })
    }
}

struct XmlMarshaller {
}

//A very simple example without recursive attributes
impl Marshaller for XmlMarshaller {
    fn marshall(&self, response: &HashMap<&str, &str>) -> String {
        return response.iter().fold(String::new(), |mut acc, (k, v)| {
            acc.push_str(&format!("<{}>{}</{}>\n", k, v, k));
            acc
        })
    }
}

struct MarshallerContext {
    strategy: Box<dyn Marshaller>
}

impl MarshallerContext {

    fn new(strategy: Box<dyn Marshaller>) -> Box<Self> {
        Box::new(MarshallerContext{ strategy })
    }

    fn marshall(&self, response: &HashMap<&str, &str>) -> String {
        //we can do some stuff here. Like select a default strategy, prepare the string etc.
        self.strategy.marshall(response)
    }

}

Sources

Design patterns

Elements of Reusable Object-Oriented Software

Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides