Blog Archives
Builder Pattern
Who does not know the StringBuilder in namespace System.Text?
The purpose of the StringBuilder is to construct a string much faster than using the operator overload “+”. Strings are immutable. Each time you use the overloaded operator, a new object is created. That slows down the creation of the final object. Here is a quick benchmark:
using System; using System.Diagnostics; using System.Text; namespace demo { class Program { static void Main(string[] args) { Stopwatch lStopwatch = new Stopwatch(); lStopwatch.Start(); string s = ""; for (int i = 0; i < 100000; i++) s += "x"; lStopwatch.Stop(); Console.WriteLine("Operator, elapsed ms: " + lStopwatch.ElapsedMilliseconds); StringBuilder lStringBuilder = new StringBuilder(); lStopwatch.Restart(); for (int i = 0; i < 100000; i++) lStringBuilder.Append("x"); lStopwatch.Stop(); Console.WriteLine("StringBuilder, elapsed ms: " + lStopwatch.ElapsedMilliseconds); Console.ReadLine(); } // } // class } // namespace
example output:
Operator, elapsed ms: 3029
StringBuilder, elapsed ms: 1
The StringBuilder supports method chaining. You can shorten your source code and write:
string s = new StringBuilder() .Append("a") .Append("b") .Append("c") .Append("d") .ToString();
instead of
StringBuilder lStringBuilder = new StringBuilder(); lStringBuilder.Append("a"); lStringBuilder.Append("b"); lStringBuilder.Append("c"); lStringBuilder.Append("d"); string s = lStringBuilder.ToString();
In general the purpose of a Builder is to separate complex object construction from its representation.
We leave the idea of strings behind and build something entirely different. This time it is not about speed, it is more about avoiding thousands of parameters in constructors and making complexity look a little bit easier. Two weeks ago I was using BMW AG to give an example for an Abstract Factory. Let’s stay loyal to cars and build classes for the production assembly.
using System; namespace demo { [Flags] public enum eRadio { GPS = 1, MP3 = 2, Screen = 4, Phone = 8 } public enum eColor { NotSet = 0, Silver, Red, Blue, White, Black } class Program { public class Car { public readonly eRadio Radio; public readonly eColor Color; public readonly double Displacement; public readonly int Doors; public readonly bool Hatchback; public readonly bool LeatherSeats; internal Car(eRadio xRadio, eColor xColor, double xDisplacement, int xDoors, bool xHatchback, bool xLeatherSeats) { Radio = xRadio; Color = xColor; Displacement = xDisplacement; Doors = xDoors; Hatchback = xHatchback; LeatherSeats = xLeatherSeats; } // constructor } // class public class CarBuilder { private eRadio _Radio = 0; private eColor _Color = eColor.NotSet; private double _Displacement = 0.0; private uint _Doors = 1; private bool? _Hatchback = null; private bool? _LeatherSeats = null; internal CarBuilder SetColor(eColor xValue) { _Color = xValue; return this; } internal CarBuilder SetDisplacement(double xValue) { _Displacement = xValue; return this; } internal CarBuilder SetDoors(uint xValue) { _Doors = xValue; return this; } internal CarBuilder SetHatchback(bool xValue) { _Hatchback = xValue; return this; } internal CarBuilder SetRadio(eRadio xValue) { _Radio = xValue; return this; } internal CarBuilder SetLeatherSeats(bool xValue) { _LeatherSeats = xValue; return this; } internal Car Create() { if (_Hatchback == null) throw new Exception("Hatchback not set"); if (_LeatherSeats == null) throw new Exception("LeatherSeats not set"); if (_Doors < 1 || _Doors > 6) throw new Exception("number of Doors invalid"); // ... etc. return new Car(_Radio, _Color, _Displacement, (int)_Doors, (bool)_Hatchback, (bool)_LeatherSeats); } } // class static void Main(string[] args) { Car lCar = new CarBuilder() .SetColor(eColor.Silver) .SetDisplacement(2.2) .SetDoors(5) .SetHatchback(true) .SetRadio(eRadio.GPS | eRadio.MP3 | eRadio.Screen) .SetLeatherSeats(true) .Create(); Console.ReadLine(); } // main } // class } // namespace