C# Design Patterns: #5 – Builder

BuilderThe Builder pattern is a narrowly-focused pattern that enables incremental creation of new products. This pattern, though limited in application, when applied correctly, brings significant power and flexibility.

The guiding concept behind the Builder pattern is separation of the actual “building” of an product from its design plan. The prototypical example of the builder pattern is saving files to multiple formats. If an application allows saving files to different formats, it will have an internal representation of the data, and will need to “build” different formats dynamically from the same source object.

In order to accomplish this, the developer creates an individual “Builder” implementation class for each file format, and then passes the Builder to a “Reader” or “Director” object that iteratively constructs the final product. The Reader will repeatedly call the Build function on the Builder for each element in the source object, until the final product will be completed.

A simple implementation of the Builder pattern might look like this:

public class Program {
public static void Main(){
Document doc = new Document();
doc.Paragraphs.Add("The quick brown fox jumps over the lazy dog.");
doc.Paragraphs.Add("Little Miss Muffet sat on a tuffet eating her curds and whey.");
DocumentReader.Parse(new DebugBuilder(),doc);
DocumentReader.Parse(new XMLBuilder("test.xml"),doc);
DocumentReader.Parse(new ASCIIBuilder("test.txt"),doc);
}
}

public class Document {
public List<string> Paragraphs = new List<string>();
}

public class DocumentReader {
public static void Parse(GenericBuilder b, Document doc){
foreach(string p in doc.Paragraphs){
b.Build(p);
}
b.Complete();
}
}

public class GenericBuilder {
public virtual void Build(string p){ }
public virtual void Complete(){ }
}

public class XMLBuilder : GenericBuilder {
public StreamWriter sw;
public XMLBuilder(string file){
sw = new StreamWriter(file);
sw.WriteLine(“<?xml version=\”1.0\”?>”);
sw.WriteLine(“<Document>”);
}
public override void Build(string p){ sw.WriteLine(“  <Paragraph>”+p+”</Paragraph>”); }
public override void Complete(){
sw.WriteLine(“</Document>”);
sw.Close();
}
}

public class ASCIIBuilder : GenericBuilder {
public StreamWriter sw;
public ASCIIBuilder(string file){ sw = new StreamWriter(file); }
public override void Build(string p){ sw.WriteLine(p); }
public override void Complete(){ sw.Close(); }
}

public class DebugBuilder : GenericBuilder {
public override void Build(string p){ Console.WriteLine(p); }
}

The Builder can save to either XML or ASCII, or alternatively display each line in the source document to the Console window for debugging. An additional function called Complete was added to the builder to enable closing open file pointers.

The most popular implementation of the Builder is in saving files. Separating the file format from the Save logic helps eliminate redundant code, simplify the saving process, and improve maintainability.

Other applications of the Builder pattern are in graphics libraries. Using Builder, an app could either draw a graphic to the screen, save it to a file, or even write out its composite parts to an XML document.

Text-to-speech engines can also leverage Builder to pronounce the same text in different voices. A Director can first convert a text string to its pronunciation components, and then Builders could convert the pronunciation to sound waves. Using this architecture, the Builder helps the text-to-speech engine easily scale the number of voices and personalities the application can provide.

Written by Andrew Palczewski

About the Author
Andrew Palczewski is CEO of apHarmony, a Chicago software development company. He holds a Master's degree in Computer Engineering from the University of Illinois at Urbana-Champaign and has over ten years' experience in managing development of software projects.
Google+

RSS Twitter LinkedIn Facebook Email

Leave a Reply

Your email address will not be published. Required fields are marked *