Monday 20 December 2010

Builder Pattern

"Separate the construction of a complex object from its representation so that the same construction process can create different representations."

This pattern uses a different ConcreteBuilder to build different representations of the same Product.  This pattern is quite similar to the Template Method pattern in that the abstract Builder class contains a series of steps that every deriving class needs to follow. In Template Method these are just a sequence of methods; in this pattern this sequence is a series of creational steps that are needed to complete construction of the Product. The Builder pattern has another layer of indirection as well because the Builder is responsible for actually creating the Product; the Builder therefore exposes a property or method that enables the constructed object to be handed back to the Director.
The pattern is diferent from Factory patterns because the Builder pattern is more concerned with construction of a 'complex' object. 
In the code example below the CoffeeVendor asks different CoffeeBuilder to build coffees, which are then returned to the client.

using System;
 
namespace Builder
{
    class Program
    {
        static void Main(string[] args)
        {
            CoffeeVendor vendor =
                new CoffeeVendor();
          
            Coffee coffee =
                vendor.ConstructCoffee(new CapuccinoBuilder());
            DisplayCoffee(coffee);
          
            coffee =
                vendor.ConstructCoffee(new EspressoBuilder());
            DisplayCoffee(coffee);
 
            coffee =
                vendor.ConstructCoffee(new LatteBuilder());
            DisplayCoffee(coffee);
 
            Console.ReadLine();
        }
 
        static void DisplayCoffee(Coffee coffee)
        {
            Console.WriteLine("Coffee = {0}, " +
            "Milk = {1}, " +
            "Water = {2}, " +
            "Topping = {3}.",
            coffee.CoffeeType,
            Enum.GetName(typeof(Coffee.Milk), coffee.MilkType),
            Enum.GetName(typeof(Coffee.WaterVolume), coffee.VolumeType),
            Enum.GetName(typeof(Coffee.Topping), coffee.ToppingType));
        }
    }
 
    /// <summary>
    /// This is our Director class - notice how the 
    /// Construct method is similar to TemplateMethod.
    /// It has the steps in it needed to construct the 
    /// object
    /// </summary>
    public class CoffeeVendor
    {
        /// <summary>
        /// Steps through the construction process and 
        /// returns the finished Product
        /// </summary>
        public Coffee ConstructCoffee(CoffeeBuilder builder)
        {
            builder.AddBeans();
            builder.AddWater();
            builder.AddMilk();
            builder.AddTopping();
 
            return builder.Coffee;
        }
    }
 
    /// <summary>
    /// This is the Product - i.e. the item constructed by the Builder.
    /// </summary>
    public class Coffee
    {
        public Coffee(string type)
        {
            CoffeeType = type;
        }
 
        public enum WaterVolume
        {
            None,
            Shot,
            HalfCup
        }
 
        public enum Milk
        {
            None,
            Hot,
            Frothy
        }
 
        public enum Topping
        {
            None,
            Chocolate,
            Cinnamon
        }
 
        public string CoffeeType { getset; }
        public WaterVolume VolumeType { getset; }
        public Topping ToppingType { getset; }
        public Milk MilkType { getset; }
        public bool BeansAdded{ getset; }      
    }
 
    /// <summary>
    /// Contains common implementation and abstract methods 
    /// provided by the derived classes
    /// </summary>
    public abstract class CoffeeBuilder
    {
        protected Coffee _coffee;
 
        public Coffee Coffee
        {
            get { return _coffee; }
        }
      
        public void AddBeans()
        {
            _coffee.BeansAdded = true;
        }
 
        public abstract void AddWater();
        public abstract void AddMilk();
        public abstract void AddTopping();
    }
 
    /// <summary>
    /// Concrete builder - builds a product in a particular way
    /// </summary>
    public class CapuccinoBuilder : CoffeeBuilder
    {
        public CapuccinoBuilder()
        {
            _coffee = new Coffee("Capuccino");
        }
 
        public override void AddWater()
        {
            _coffee.VolumeType = Coffee.WaterVolume.HalfCup;              
        }
 
        public override void AddMilk()
        {
            _coffee.MilkType = Coffee.Milk.Hot;
        }
 
        public override void AddTopping()
        {
            _coffee.ToppingType = Coffee.Topping.Chocolate;
        }
    }
 
    /// <summary>
    /// Concrete builder - builds a product in a particular way
    /// </summary>
    public class LatteBuilder : CoffeeBuilder
    {
        public LatteBuilder()
        {
            _coffee = new Coffee("Latte");
        }
 
        public override void AddWater()
        {
            _coffee.VolumeType = Coffee.WaterVolume.None;
        }
 
        public override void AddMilk()
        {
            _coffee.MilkType = Coffee.Milk.Hot;
        }
 
        public override void AddTopping()
        {
            _coffee.ToppingType = Coffee.Topping.Cinnamon;
        }
    }
 
    /// <summary>
    /// Concrete builder - builds a product in a particular way
    /// </summary>
    public class EspressoBuilder : CoffeeBuilder
    {
        public EspressoBuilder()
        {
            _coffee = new Coffee("Espresso");
        }
        public override void AddWater()
        {
            _coffee.VolumeType = Coffee.WaterVolume.Shot;
        }
 
        public override void AddMilk()
        {
            _coffee.MilkType = Coffee.Milk.None;
        }
 
        public override void AddTopping()
        {
            _coffee.ToppingType = Coffee.Topping.None;
        }
    }
}
Outputs:
Coffee = Capuccino, Milk = Hot, Water = HalfCup, Topping = Chocolate.
Coffee = Espresso, Milk = None, Water = Shot, Topping = None.
Coffee = Latte, Milk = Hot, Water = None, Topping = Cinnamon.

No comments:

Post a Comment