DEV Community

AK
AK

Posted on

Strategy

What is Strategy design pattern?

Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.


Class Diagram

Class Diagram

Context is composed of a Strategy. Instead of implementing a behavior the Context delegates it to Strategy. The context would be the class that would require changing behaviors. We can change behavior dynamically. Strategy is implemented as interface so that we can change behavior without affecting our context.


Advantages

  • A family of algorithms can be defined as a class hierarchy and can be used interchangeably to alter application behavior without changing its architecture.
  • By encapsulating the algorithm separately, new algorithms complying with the same interface can be easily introduced.
  • The application can switch strategies at run-time.
  • Strategy enables the clients to choose the required algorithm, without using a “switch” statement or a series of “if-else” statements.
  • Data structures used for implementing the algorithm are completely encapsulated in Strategy classes. Therefore, the implementation of an algorithm can be changed without affecting the Context class.

Disadvantages

  • The application must be aware of all the strategies to select the right one for the right situation.
  • Context and the Strategy classes normally communicate through the interface specified by the abstract Strategy base class. Strategy base class must expose interface for all the required behaviours, which some concrete Strategy classes might not implement.
  • In most cases, the application configures the Context with the required Strategy object. Therefore, the application needs to create and maintain two objects in place of one.

Functional approach

Functions and closures simplify Strategy implementation as you can inject behavior right into the object without complex interface definition.

It seems that Strategy is often implicitly and widely used in the modern development with Rust, e.g. it’s just like iterators work:

functional.rs

type RouteStrategy = fn(from: &str, to: &str);

fn walking_strategy(from: &str, to: &str) {
    println!("Walking route from {} to {}: 4 km, 30 min", from, to);
}

fn public_transport_strategy(from: &str, to: &str) {
    println!(
        "Public transport route from {} to {}: 3 km, 5 min",
        from, to
    );
}

struct Navigator {
    route_strategy: RouteStrategy,
}

impl Navigator {
    pub fn new(route_strategy: RouteStrategy) -> Self {
        Self { route_strategy }
    }

    pub fn route(&self, from: &str, to: &str) {
        (self.route_strategy)(from, to);
    }
}

fn main() {
    let navigator = Navigator::new(walking_strategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");

    let navigator = Navigator::new(public_transport_strategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");

    let navigator = Navigator::new(|from, to| println!("Specific route from {} to {}", from, to));
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)