I just followed Rust Design Pattern - Strategy (aka Policy).
Make code abstract as possible.
… given an algorithm solving a particular problem, we define only the skeleton of the algorithm at an abstract level, and we separate the specific algorithm’s implementation into different parts.
The stragety pattern are used frequently with dependency inversion (it is also related with high level modules).
use std::collections::HashMap;
type Data = HashMap<String, u32>;
trait Formatter {
fn format(&self, data: &Data, buf: &mut String);
}
struct Report;
impl Report {
fn generate<T: Formatter>(g: T, s: &mut String) {
let mut data = HashMap::new();
data.insert("one".to_string(), 1);
data.insert("two".to_string(), 2);
g.format(&data, s);
}
}
struct Text;
impl Formatter for Text {
fn format(&self, data: &Data, buf: &mut String) {
for (k, v) in data {
let entry = format!("{} {}\n", k, v);
buf.push_str(&entry);
}
}
}
struct Json;
impl Formatter for Json {
fn format(&self, data: &Data, buf: &mut String) {
buf.push('[');
for (k, v) in data.into_iter() {
let entry = format!(r#"{{"{}":"{}"}}"#, k, v);
buf.push_str(&entry);
buf.push(',');
}
buf.pop(); // remove extra , at the end
buf.push(']');
}
}
fn main() {
let mut s = String::from("");
Report::generate(Text, &mut s);
// s = "two 2\none 1\n"
assert!(s.contains("one 1"));
assert!(s.contains("two 2"));
s.clear(); // reuse the same buffer
Report::generate(Json, &mut s);
// s = [{"one":"1"},{"two":"2"}]
assert!(s.contains(r#"{"one":"1"}"#));
assert!(s.contains(r#"{"two":"2"}"#));
}
main()
function, user use Report::generate
function, and this is the only function user uses.strct
of its first parameter. In this case, Text
or Json
.Formatter
which has format
method.
If you implement the trait for the structs, Text
and Json
, differently and generate
function takes the Formatter
-bounded struct, the only thing generater
should care about is “calling format
method of the first parameter struct”.generate
method.generate
function point, the actual type of its first parameter (Text
or Json
) is called stragety.generate
function is an abstract function because it doesn’t know how strategies implement format
.CSV
and implement format
for this struct so that generate
can use this stragety.