tags:

views:

837

answers:

5

Below is the code I have tried, is there a better way to do this?

    public class NutritionFacts
    {
        public static NutritionFacts.Builder Build(string name, int servingSize, int servingsPerContainer)
        {
            return new NutritionFacts.Builder(name, servingSize, servingsPerContainer);
        }

        public sealed class Builder
        {
            public Builder(String name, int servingSize,
            int servingsPerContainer)
            {
            }
            public Builder totalFat(int val) { }
            public Builder saturatedFat(int val) { }
            public Builder transFat(int val) { }
            public Builder cholesterol(int val) { }
            //... 15 more setters
            public NutritionFacts build()
            {
                return new NutritionFacts(this);
            }
        }
        private NutritionFacts(Builder builder) { }
        protected NutritionFacts() { }
    }
  • How do we extend such a class? Do we need to write separate builder classes for each of the derived classes?

    public class MoreNutritionFacts : NutritionFacts
    {
        public new static MoreNutritionFacts.Builder Build(string name, int servingSize, int servingsPerContainer)
        {
            return new MoreNutritionFacts.Builder(name, servingSize, servingsPerContainer);
        }
        public new sealed class Builder
        {
            public Builder(String name, int servingSize,
            int servingsPerContainer) {}
            public Builder totalFat(int val) { }
            public Builder saturatedFat(int val) { }
            public Builder transFat(int val) { }
            public Builder cholesterol(int val) { }
            //... 15 more setters
            public Builder newProperty(int val) { }
            public MoreNutritionFacts build()
            {
                return new MoreNutritionFacts(this);
            }
        }
        private MoreNutritionFacts(MoreNutritionFacts.Builder builder) { }
    }
    
+2  A: 

In Protocol Buffers, we implement the builder pattern like this (vastly simplified):

public sealed class SomeMessage
{
  public string Name { get; private set; }
  public int Age { get; private set; }

  // Can only be called in this class and nested types
  private SomeMessage() {}

  public sealed class Builder
  {
    private SomeMessage message = new SomeMessage();

    public string Name
    {
      get { return message.Name; }
      set { message.Name = value; }
    }

    public int Age
    {
      get { return message.Age; }
      set { message.Age = value; }
    }

    public SomeMessage Build()
    {
      // Check for optional fields etc here
      SomeMessage ret = message;
      message = null; // Builder is invalid after this
      return ret;
    }
  }
}

This isn't quite the same as the pattern in EJ2, but:

  • No data copying is required at build time. In other words, while you're setting the properties, you're doing so on the real object - you just can't see it yet. This is similar to what StringBuilder does.
  • The builder becomes invalid after calling Build() to guarantee immutability. This unfortunately means it can't be used as a sort of "prototype" in the way that the EJ2 version can.
  • We use properties instead of getters and setters, for the most part - which fits in well with C# 3's object initializers.
  • We do also provide setters returning this for the sake of pre-C#3 users.

I haven't really looked into inheritance with the builder pattern - it's not supported in Protocol Buffers anyway. I suspect it's quite tricky.

Jon Skeet
>> "which fits in well with C# 3's object initializers."In this instances will be created as new SomeMessage.Build(Name="GK", Age=0). But its as good as writing new SomeMessage(Name="GK", Age=0) right? In this case do we need build method? How will it help us to avoid telescoping constructors?
gk
No, it will be written as new SomeMessage.Builder { Name="GK", Age = 0 }. Note the difference in brackets vs braces. The difference is that you only need to specify the properties you want. Note that with C# 4, a single constructor with lots of optional parameters will provide another option.
Jon Skeet
Ok. Now I got it. new SomeMessage{Name="GK", Age=0} is not possible the properties are private, only builder can acces it.
gk
The setters are private, yes, for the sake of immutability. The getters are public, of course.
Jon Skeet
Wouldn't you want to give SomeMessage a private constructor?
cdmckay
Oops, yes - will add...
Jon Skeet
+3  A: 

This blog entry might be of interest

A neat variation on the pattern in C# is the use of an implicit cast operator to make the final call to Build() unnecessary:

public class CustomerBuilder
{

   ......     

   public static implicit operator Customer( CustomerBuilder builder ) 
   {  
      return builder.Build();
   } 
}
alasdairg
That's a nice trick.
gk
I've used the code (but slightly modified) from the blog post in a number of production systems and think it works very well
Kane
+2  A: 

I recently implemented a version that is working out nicely.

Builders are factories which cache the most recent instance. Derived builders create instances and clear the cache when properties change.

The framework is straightforward:

public interface IFactory<T>
{
    T Create();
}

public interface IBuilder<T> : IFactory<T>
{
    bool HasInstance { get; }

    T Instance { get; }

    void ClearInstance();
}

public abstract class Builder<T> : IBuilder<T>
{
    public static implicit operator T(Builder<T> builder)
    {
        return builder.Instance;
    }

    T _instance;

    public abstract T Create();

    public bool HasInstance { get; private set; }

    public T Instance
    {
        get
        {
            if(!this.HasInstance)
            {
                _instance = Create();

                this.HasInstance = true;
            }

            return _instance;
        }
    }

    public void ClearInstance()
    {
        this.HasInstance = false;

        _instance = default(T);
    }
}

The problem is harder to understand. Let's say our domain has the concept of an Order:

public class Order
{
    public string ReferenceNumber { get; private set; }

    public DateTime? ApprovedDateTime { get; private set; }

    public void Approve()
    {
        this.ApprovedDateTime = DateTime.Now;
    }
}

ReferenceNumber does not change after creation, so we model it read-only via the constructor:

public Order(string referenceNumber)
{
    // ... validate ...

    this.ReferenceNumber = referenceNumber;
}

How do we reconstitute an existing conceptual Order from, say, database data?

This is the root of the ORM disconnect: we shouldn't put public setters on ReferenceNumber and ApprovedDateTime because it is technically convenient. (We also shouldn't mark them virtual if not meant for extension.)

A Builder with special knowledge is a useful pattern. An alternative to nested types would be internal access. It enables mutability, domain behavior (POCO), and, as a bonus, the "prototype" pattern mentioned by Jon Skeet.

First, add an internal constructor to Order:

internal Order(string referenceNumber, DateTime? approvedDateTime)
{
    this.ReferenceNumber = referenceNumber;
    this.ApprovedDateTime = approvedDateTime;
}

Then, add a Builder with mutable properties:

public class OrderBuilder : Builder<Order>
{
    string _referenceNumber;
    DateTime? _approvedDateTime;

    public override Order Create()
    {
        return new Order(_referenceNumber, _approvedDateTime);
    }

    public string ReferenceNumber
    {
        get { return _referenceNumber; }
        set
        {
            if(value != _referenceNumber)
            {
                _referenceNumber = value;

                ClearInstance();
            }
        }
    }

    public DateTime? ApprovedDateTime
    {
        get { return _approvedDateTime; }
        set
        {
            if(value != _approvedDateTime)
            {
                _approvedDateTime = value;

                ClearInstance();
            }
        }
    }
}

Finally, when you need to reconstitute an Order, use OrderBuilder with the implicit cast:

Order order = new OrderBuilder
{
    ReferenceNumber = "ABC123",
    ApprovedDateTime = new DateTime(2008, 11, 25)
};

This got really long. Hope it helps!

Bryan Watts
Thanks for the reply Bryan. It's indeed a very clean implementation of the pattern. Jon, alasdairg and Jaime also have some simillar/different implementations. I personally prefer to have static Builder Method in the type, which returns the builder class.
gk
I am still trying to figure out how to extend such a builder class. If we have a SpecialOrder (bad name!!), then we have to go for SpecialOrderBuilder but in that the case the user (some one is using your code) should know the builder implemenations of each one.
gk
In my example above I am repeating the code in MoreNutritionFacts.Builder, which is also not good. Is there a more clean/correct implemenation for this? As Jon said, it seems to be tricky.
gk
NutritionFactsBuilder contains all the properties for NutritionFacts. If you derive MoreNutritionFactsBuilder from it, you inherit all of those definitions and just have to override Create(). No repeating code.
Bryan Watts
Also, just because the classes are related doesn't mean they should be nested. An association pattern allows for more flexibility. Think [Builder(typeof(NutritionFactsBuilder))] on the definition of NutritionFacts. What benefit does nesting the type offer?
Bryan Watts
I just realized the inheritance won't work: you would ultimately want to derive from Builder<MoreNutritionFacts>. I'll think about that.
Bryan Watts
A: 

The reason to use Joshua Bloch's builder pattern was to create a complex object out of parts, and also to make it immutable.

In this particular case, using optional, named parameters in C# 4.0 is cleaner. You give up some flexibility in design (don't rename the parameters), but you get better maintainable code, easier.

If the NutritionFacts code is:

  public class NutritionFacts
  {
    public int servingSize { get; private set; }
    public int servings { get; private set; }
    public int calories { get; private set; }
    public int fat { get; private set; }
    public int carbohydrate { get; private set; }
    public int sodium { get; private set; }

    public NutritionFacts(int servingSize, int servings, int calories = 0, int fat = 0, int carbohydrate = 0, int sodium = 0)
    {
      this.servingSize = servingSize;
      this.servings = servings;
      this.calories = calories;
      this.fat = fat;
      this.carbohydrate = carbohydrate;
      this.sodium = sodium;
    }
  }

Then a client would use it as

 NutritionFacts nf2 = new NutritionFacts(240, 2, calories: 100, fat: 40);

If the construction is more complex this would need to be tweaked; if the "building" of calories is more than putting in an integer, it's conceivable that other helper objects would be needed.

Marc Meketon