Skip to main content

Command Palette

Search for a command to run...

Strategy pattern

Updated
2 min read

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

Design Patterns

Part 1 of 2

In this series, I will publish articles related to my design patterns studies. I am using this blog as a tool to learn more efficiently and hopefully will help others as well.

Up next

Composite

With this pattern, we can model object/structs hierarchies independently if the object is simple or composed it will be treated uniformly