In the previous two articles, we were talking about Builder Design Pattern and Fluent Builder With Recursive Generics. We recommend reading at least the first one for a better understanding of the Builder Design Pattern. The second one is the upgrade to the first article and if you want to learn more about using recursive generics with the Builder Pattern, then we recommend reading that one as well.
Why do we need the Faceted Builder?
Sometimes we may have a complex object, and the creational process requires more than one builder class. So, what we need to do is to introduce multiple builder classes in such a way, that we can jump from one builder to another while creating our object.
The faceted Builder approach helps us a lot in that process because we create a facade over our builders and it allows us to use all the builders to create a single object.
Let’s learn how to do that.
The source code is available at Faceted Builder – Source Code.
For the main page of this series check out C# Design Patterns.
Faceted Builder Implementation
We are going to start with a “complex” object model:
public class Car { public string Type { get; set; } public string Color { get; set; } public int NumberOfDoors { get; set; } public string City { get; set; } public string Address { get; set; } public override string ToString() { return $"CarType: {Type}, Color: {Color}, Number of doors: {NumberOfDoors}, Manufactured in {City}, at address: {Address}"; } }
We have the info part and the address part of our object, so we are going to use two builders to create this whole object.
As we said, we need a facade, so, let’s start with that:
public class CarBuilderFacade { protected Car Car { get; set; } public CarBuilderFacade() { Car = new Car(); } public Car Build() => Car; }
We instantiate the Car
object, which we want to build and expose it through the Build method.
What we need now is to create concrete builders. So, let’s start with the CarInfoBuilder
which needs to inherit from the facade class:
public class CarInfoBuilder: CarBuilderFacade { public CarInfoBuilder(Car car) { Car = car; } public CarInfoBuilder WithType(string type) { Car.Type = type; return this; } public CarInfoBuilder WithColor(string color) { Car.Color = color; return this; } public CarInfoBuilder WithNumberOfDoors(int number) { Car.NumberOfDoors = number; return this; } }
As we can see, we receive, through the constructor, an object we want to build and use the fluent interface for building purpose.
Let’s do the same for the CarAddresBuilder
class:
public class CarAddressBuilder: CarBuilderFacade { public CarAddressBuilder(Car car) { Car = car; } public CarAddressBuilder InCity(string city) { Car.City = city; return this; } public CarAddressBuilder AtAddress(string address) { Car.Address = address; return this; } }
At this moment we have both builder classes, but we can’t start building our object yet because we haven’t exposed our builders inside the facade class. Well, let’s do that:
public class CarBuilderFacade { protected Car Car { get; set; } public CarBuilderFacade() { Car = new Car(); } public Car Build() => Car; public CarInfoBuilder Info => new CarInfoBuilder(Car); public CarAddressBuilder Built => new CarAddressBuilder(Car); }
As of this moment, we can start building our object:
class Program { static void Main(string[] args) { var car = new CarBuilderFacade() .Info .WithType("BMW") .WithColor("Black") .WithNumberOfDoors(5) .Built .InCity("Leipzig ") .AtAddress("Some address 254") .Build(); Console.WriteLine(car); } }
And that is it. We have built our object with the Faceted Builder approach.
Conclusion
With this article, we are finished with the small Builder Design Pattern series, but we have more articles to come about Design Patterns in C#. So stay tuned.
Hi Marinko. We could remove the below constructors entirely and the code would still work. Am I right?
Thank you for the article.
In this specific case, it would work. But if you want to work with only a specific builder without a facade, then you will need that constructor.
No Need to Initialize Car in the derived class. The Car property setter can be made private and can still be initialized in the base class by calling base class constructor.
public class CarAddressBuilder: CarBuilderFacade
{
public CarAddressBuilder(Car car):base(car)
{
//Car = car; //this is not really needed
}
Hello A Muslim. Well, if you do something like this, you would have to have a car object inside the CarBuilderFacade constructor, but as you can see in the Program.cs class, we are using the constructor without parameters. Of course your solution would work if you add something like this:
var car = new CarBuilderFacade(new Car())
So, I would say it is all based on your preference, how you want to implement your solution. Anyhow, thank you a lot for your suggestion and for reading this article. Best regards.
There are two problem with this implementation:
There nothing to stop me from doing this, leaving me with a car with no type or color.
var builder = new CarBuilderFacade();
var coupe = builder
.Info.
.WithNumberOfDoors(2)
.Build();
Further, there’s also nothing to stop me from then doing this, leaving both sedan & coupe referring to the same 4-door car.
var sedan = builder
.Info.
.WithNumberOfDoors(4)
.Build();
Hello James. Well we could go even further and to expose just a wrapper around this creation to the client and to require all the data with a constructor (or any other variation). But, this is not the point here. We just show how can we combine two different builder classes to create a single complex object. Still, thank you for your comment and suggestion as well.