Builder Design Pattern in Rust

The problem to Solve :

  • Some objects are simple and can be created using a single constructor call but there is some constructor which may need more parameter to construct the object.
  • Having more parameters to a constructor is not a good approach to handle the product

How Builder Patterns Solves it

  • We create small objects to create the final object
  • Construct an object in multiple pieces but making sure the end result is the same
  • A builder design pattern provides an API for constructing an Object step-by-step
When piecewise object constructon is complicated, provide an API for doing it in simple & clear manner

use std::io::prelude::*;
use std::fs::File;
use std::env;
use std::process;

struct Director {
    builder: Box<Builder>,
}

impl Director {
    fn new(builder: Box<Builder>) -> Director {
        Director {
            builder: builder,
        }
    }
   
    fn construct(&mut self) {
        self.builder.make_title("Greeting".to_string());
        self.builder.make_string("朝から昼にかけて".to_string());
        self.builder.make_items(vec!["おはようございます。".to_string(), "こんにちは。".to_string()]);
        self.builder.make_string("夜に".to_string());
        self.builder.make_items(vec!["こんばんは。".to_string(), "おやすみなさい。".to_string(), "さようなら。".to_string()]);
        self.builder.close();
    }
}

trait Builder {
    fn make_title(&mut self, title: String);
    fn make_string(&mut self, string: String);
    fn make_items(&mut self, items: Vec<String>);
    fn close(&mut self);
    fn get_result(&self) -> String;
}

struct TextBuilder {
    buffer: String,
}

impl TextBuilder {
    fn new() -> TextBuilder {
        TextBuilder {
            buffer: String::new(),
        }
    }
}

impl Builder for TextBuilder {
    fn make_title(&mut self, title: String) {
        self.buffer.push_str("============================== ");
        self.buffer.push_str(&format!("『{}』 ", title));
        self.buffer.push_str(" ");
   }

    fn make_string(&mut self, s: String) {
        self.buffer.push_str(&format!("■{} ", s));
        self.buffer.push_str(" ");
    }

    fn make_items(&mut self, items: Vec<String>) {
        for item in &items {
            self.buffer.push_str(&format!(" ・{} ", item));
        }
        self.buffer.push_str(" ");
    }

    fn close(&mut self) {
        self.buffer.push_str("============================== ");
    }

    fn get_result(&self) -> String {
        self.buffer.clone()
    }
}

struct HTMLBuilder {
    file_name: String,
    writer: File,
}

impl HTMLBuilder {
    fn new(title: String) -> HTMLBuilder {
        let file_name = format!("{}.html", title);
        let writer = File::create(file_name.clone()).expect("Unable to create file");

        HTMLBuilder {
            file_name: file_name,
            writer: writer,
        }
    }
}

#[allow(unused_must_use)]
impl Builder for HTMLBuilder {
    fn make_title(&mut self, title: String) {
        writeln!(self.writer, "<html><head><title>{}</title></head><body>", title);
        writeln!(self.writer, "<h1>{}</h1>", title);
    }

    fn make_string(&mut self, string: String) {
        writeln!(self.writer, "<p>{}</p>", string);
    }

    fn make_items(&mut self, items: Vec<String>) {
        writeln!(self.writer, "<ul>");
        for item in &items {
            writeln!(self.writer, "<li>{}</li>", item);
        }
        writeln!(self.writer, "</ul>");
    }

    fn close(&mut self) {
        writeln!(self.writer, "</body></html>");
        self.writer.flush();
    }

    fn get_result(&self) -> String {
        self.file_name.clone()
    }
}

fn usage() {
    println!("Usage: cargo run plain");
    println!("Usage: cargo run html");
}

fn main() {
    let args: Vec<String> = env::args().collect();
   
    if args.len()-1 != 1 {
        usage();
        process::exit(0);
    }

    match args[1].as_str() {
        "plain" => {
            let text_builder = Box::new(TextBuilder::new());
            let mut director = Director::new(text_builder);
            director.construct();
            let result = director.builder.get_result();
            println!("{}", result);
        },
        "html" => {
            let html_builder = Box::new(HTMLBuilder::new("test".to_string()));
            let mut director = Director::new(html_builder);
            director.construct();
            let result = director.builder.get_result();
            println!("{}", result);
        },
        _ => {
            usage();
            process::exit(0);
        },
    }
   
}
About Author :

I am Pavankumar, Having 8.5 years of experience currently working in Video/Live Analytics project.

Comment / Suggestion Section
Point our Mistakes and Post Your Suggestions