Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement and extend Joshua's builder pattern in .net?

Tags:

c#

.net

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) { }
    }
    
like image 627
gk. Avatar asked Nov 24 '08 09:11

gk.


People also ask

When would you use the builder pattern why not just use a factory pattern?

The builder pattern, as the name implies, is an alternative way to construct complex objects. This pattern should be used when we want to build different immutable objects using the same object building process. 3.

What does builder () build () do?

Builder is a creational design pattern, which allows constructing complex objects step by step. Unlike other creational patterns, Builder doesn't require products to have a common interface. That makes it possible to produce different products using the same construction process.


2 Answers

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.

like image 125
Jon Skeet Avatar answered Oct 21 '22 19:10

Jon Skeet


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();
   } 
}
like image 42
alasdairg Avatar answered Oct 21 '22 20:10

alasdairg