| Type | Creational |
| Purpose | Separate the construction of a complex object from its representation so that the same construction process can create different representations. |
Description
The Builder pattern separates the construction of a complex object from its representation. It is based on the collaboration between a Director and one or more Builder implementations. Any number of concrete builders can conform to a common Builder interface, and each may be used by the Director to construct a Product according to a particular specification.
The builder supplies the individual parts of the product step by step, under the director's control, until construction is complete. Different concrete builders can follow the same construction process while producing different representations or assemblies of the product.
In this way, the client can rely on a consistent construction process without being coupled to the details of how the product is assembled internally. The completed product is returned only when the building process is finished. The Builder pattern is primarily concerned with how a complex object is constructed.
Overview
| Builder | An interface defining what can be built. |
| Director | Defines the sequence of operations used to construct a Product. |
| ConcreteBuilder | Implements the Builder interface and constructs the parts of the Product. |
| Product | The complex object being constructed. |
| Client | A class that calls a Director to construct a Product using a Builder. |
Implementation
In this example, we'll create a product that translates from English into Chinese or French. The product will work with HTML or text. The various parts include interpreters, parsers, and renderers.
We'll just create two builders to keep the code concise.
// Parts
public interface IParser
{
string Parse(string text);
}
public class TextParser : IParser
{
public string Parse(string text)
=> text.Trim().ToLowerInvariant();
}
public class HtmlParser : IParser
{
public string Parse(string text)
{
text = text.Trim().ToLowerInvariant();
if (text.StartsWith("<p>") && text.EndsWith("</p>"))
{
return text[3..^4];
}
return "";
}
}
public interface IInterpreter
{
string Interpret(string text);
}
public class ChineseInterpreter : IInterpreter
{
public string Interpret(string text)
=> text switch
{
"hello" => "你好",
"world" => "世界",
_ => "不知道"
};
}
public class FrenchInterpreter : IInterpreter
{
public string Interpret(string text)
=> text switch
{
"hello" => "bonjour",
"world" => "monde",
_ => "je ne sais pas"
};
}
public interface IRenderer
{
void Render(string text);
}
public class TextRenderer : IRenderer
{
public void Render(string text)
=> Console.WriteLine(text);
}
public class HtmlRenderer : IRenderer
{
public void Render(string text)
=> Console.WriteLine($"<p>{text}</p>");
}
// Product
public class EnglishTranslator
{
public IParser Parser { get; set; }
public IInterpreter Interpreter { get; set; }
public IRenderer Renderer { get; set; }
public void Translate(string english)
{
var parsed = Parser.Parse(english);
var translation = Interpreter.Interpret(parsed);
Renderer.Render(translation);
}
}
// Builder
public interface IBuilder
{
void BuildParser();
void BuildInterpreter();
void BuildRenderer();
EnglishTranslator GetResult();
}
// Concrete Builders
public class EnglishChineseTextTranslatorBuilder : IBuilder
{
private readonly EnglishTranslator _translator = new();
public void BuildParser()
=> _translator.Parser = new TextParser();
public void BuildInterpreter()
=> _translator.Interpreter = new ChineseInterpreter();
public void BuildRenderer()
=> _translator.Renderer = new TextRenderer();
public EnglishTranslator GetResult()
=> _translator;
}
public class EnglishFrenchHtmlTranslatorBuilder : IBuilder
{
private readonly EnglishTranslator _translator = new();
public void BuildParser()
=> _translator.Parser = new HtmlParser();
public void BuildInterpreter()
=> _translator.Interpreter = new FrenchInterpreter();
public void BuildRenderer()
=> _translator.Renderer = new HtmlRenderer();
public EnglishTranslator GetResult()
=> _translator;
}
// Director
public class Director(IBuilder builder)
{
public void Construct()
{
builder.BuildParser();
builder.BuildInterpreter();
builder.BuildRenderer();
}
}
// Demonstration
public static class BuilderDemo
{
public static void Execute()
{
// english to chinese
var chineseTranslatorBuilder = new EnglishChineseTextTranslatorBuilder();
var director = new Director(chineseTranslatorBuilder);
director.Construct();
var translator = chineseTranslatorBuilder.GetResult();
translator.Translate("Hello");
// english to french
var frenchTranslatorBuilder = new EnglishFrenchHtmlTranslatorBuilder();
director = new Director(frenchTranslatorBuilder);
director.Construct();
translator = frenchTranslatorBuilder.GetResult();
translator.Translate("<p>Hello</p>");
}
}
// outputs:
//
// 你好
// <p>bonjour</p>
Notes
In some examples of Builder, the product contains a list of its parts. In our case, the properties of the product are our parts. To pass different builders to the same director, we can pass the builder via the construct method rather than the constructor.
The updated Director and Demonstration would look like this:
public class Director
{
public void Construct(IBuilder builder)
{
builder.BuildParser();
builder.BuildInterpreter();
builder.BuildRenderer();
}
}
public static class BuilderDemo
{
public static void Execute()
{
var chineseTranslatorBuilder = new EnglishChineseTextTranslatorBuilder();
var frenchTranslatorBuilder = new EnglishFrenchHtmlTranslatorBuilder();
var director = new Director();
director.Construct(chineseTranslatorBuilder);
var translator = chineseTranslatorBuilder.GetResult();
translator.Translate("Hello");
director.Construct(frenchTranslatorBuilder);
translator = frenchTranslatorBuilder.GetResult();
translator.Translate("<p>Hello</p>");
}
}
Summary
Design patterns and their implementation details should be treated as guidelines and tools, and they should ultimately serve the specific needs of your application. There's no universally correct answer—it depends on your particular case and application requirements.
For more information, please see here.