I’ve started to construct a builder so that I can easily create test data for unit tests I am writing.
The basic structure of the builder is:
public class MyClassBuilder
{
public int id = 0; //setting the default value here
//allows MyClass to be built with a specific id
public MyClassBuilder WithId(int id)
{
this.id = id;
return this;
}
public MyClass Build()
{
return new MyClass(id);
}
}
The usage of this pattern then becomes:
MyClass mc = new MyClassBuilder().WithId(5).Build();
I’m comfortable with that…but where I have questions is when MyClass has a property that is non trivial….I’m a little uncertain about how to go about constructing it with a default value.
public class MyClassBuilder
{
public int id = 0; //setting the default value here
//do I construct the default value using a MySecondClassBuilder?
public MySecondClass mySecondClass;
//allows MyClass to be built with a specific id
public MyClassBuilder WithId(int id)
{
this.id = id;
return this;
}
public MyClassBuilder WithMySecondClass(MySecondClass mySecondClass)
{
this.mySecondClass = mySecondClass;
}
public MyClass Build()
{
return new MyClass(id);
}
}
My assumption is that I would create a builder for MySecondClass and use that to create the default implementation.
Can anyone confirm that my assumption is correct and is the best practice?
I’m currently in the process of testing out my assumption but I thought I’d document this idea on StackOverflow since the only examples of the builder patter that I could find using google only ever constructed properties that are value types and not reference types.
As with most things – it depends.
If you want reference semantics in the mySecondClass field (ie: you want to save a reference to another class), then it’s exactly like your integer. Having the default be null could be perfectly acceptable.
However, if you want to always guarantee a value, you’ll need to either build or just construct a new object for your second class.
Also, if you want cloning instead of by reference copies, you’d need to construct a new object and assign it to the reference.
That being said, in many cases, traditional constructors are more appropriate than trying to do a fluent build-style interface. It’s really easy to “mess up” a fluent interface for building like this, since you’re relying on the user more than with a traditional constructor.
As an example, in your code, what happens if the user never calls Build? It’s still returning something (since each of the methods returns an object), but is it valid? I wouldn’t be sure as a consumer of your class. Traditional constructors with overloads, on the other hand, show me exactly what is valid and what is not valid, and are compile-time enforced.