Introduction
All right, folks, welcome to a new article on Builder Design Pattern.
A builder pattern should only be used for creating complex objects, like when object creation needs to be separated from its assembly, such as trees. I have explained that in my other blog
Composite And Builder Design Pattern With A Tree. Feel free to visit it if you wish to learn that with a composite pattern.
Our recipe needs the following ingredients:
- Adding a builder interface
This abstract base class defines all of the steps that must be taken in order to correctly create a product. For our example, interface IBuildMobile,
- Creating a concrete builder class
These classes contain the functionality to create a particularly complex product. For our example, the class Apple & class OnePlus,
- Implementing a director
The director-class controls the algorithm that generates the final product. A director object injects the product through the constructor. The director then calls methods of the concrete builder in the correct order to generate the product. On completion of the process, the GetProduct method of the builder object can be used to return the product. For our example, class Mobile & method which returns the mobile is GetMobile() in our example.
The builder pattern aims to separate the construction of a complex object from its representation so that the same construction process can create different representations.
Now you probably are scratching your head wondering what in the world was that?
Here is what I was trying to convey... The builder pattern removes any and all construction or initialization code from an object class and abstracts it out to an interface.
It is used to construct a complex object step-by-step and the final step will return the object of the final product.
Ok! Enough talk! Let's see how UML would look like for the example which we are going to design.
Let's have 2 mobile-phone's implementation: OnePlus & Apple, and have a client to only worry about calling these classes rather than worrying about what is there in mobile and how it supposed to get it.
Now imagine that you have to make the Mobile class's object. You need to have mobile's specifications, plus which mobile is what( OnePlus or Apple) & blah blah blah.
Why does the client have to worry about any of that?
Let's begin with interface IMobileRequirements. It defines all the requirements your mobile is supposed to have.
- public interface IMobileRequirements
- {
- void SetModelName(string modelName);
- void SetProcessor(string processor);
- void SetDisplayResolution(string resolution);
- void SetRAM(int RAM);
- void SetROM(int ROM);
- }
Now it's concrete implementation: class MobileRequirements which sets all the basic requirements of mobile.
- public class MobileRequirements : IMobileRequirements
- {
- public string ModelName { get; set; }
- public string Processor { get; set; }
- public string DisplayResolution { get; set; }
- public int RAM { get; set; }
- public int ROM { get; set; }
- public void SetDisplayResolution(string resolution)
- {
- this.DisplayResolution = resolution;
- }
-
- public void SetModelName(string modelName)
- {
- this.ModelName = modelName;
- }
-
- public void SetProcessor(string processor)
- {
- this.Processor = processor;
- }
-
- public void SetRAM(int RAM)
- {
- this.RAM = RAM;
- }
-
- public void SetROM(int ROM)
- {
- this.ROM = ROM;
- }
- }
Now our builder interface: IBuildMobile
- public interface IBuildMobile
- {
- void BuildModelName();
- void BuildProcessor();
- void BuildDisplayResolution();
- void BuildRAM();
- void BuildROM();
- MobileRequirements GetMobileRequirements();
- }
A concrete representation of IBuildMobile. Basically our concrete builder classes.
class OnePlus
- namespace Builder_Design_Pattern
- {
- public class OnePlus : IBuildMobile
- {
- public MobileRequirements specifications = new MobileRequirements();
- public void BuildDisplayResolution()
- {
- specifications.SetDisplayResolution("3168 * 1440");
- }
-
- public void BuildProcessor()
- {
- specifications.SetProcessor("Qualcomm Snapdragon 865");
- }
-
- public void BuildRAM()
- {
- specifications.SetRAM(8);
- }
-
- public void BuildROM()
- {
- specifications.SetROM(128);
- }
-
- public MobileRequirements GetMobileRequirements()
- {
- return this.specifications;
- }
-
- public void BuildModelName()
- {
- specifications.SetModelName("One Plus 8 Pro");
- }
- }
- }
class Apple
- namespace Builder_Design_Pattern
- {
- public class Apple : IBuildMobile
- {
- public MobileRequirements Specifications = new MobileRequirements();
-
- public void BuildDisplayResolution()
- {
- Specifications.SetDisplayResolution("828 * 1792");
- }
-
- public void BuildProcessor()
- {
- Specifications.SetProcessor("Apple A13 Bionic");
- }
-
- public void BuildRAM()
- {
- Specifications.SetRAM(4);
- }
-
- public void BuildROM()
- {
- Specifications.SetROM(128);
- }
-
- public MobileRequirements GetMobileRequirements()
- {
- return this.Specifications;
- }
-
- public void BuildModelName()
- {
- Specifications.SetModelName("iphone 11");
- }
- }
- }
Now our final director: class Mobile.
- public class Mobile
- {
- public IBuildMobile CellPhone;
- public Mobile(IBuildMobile cellPhone)
- {
- this.CellPhone = cellPhone;
- }
-
- public MobileRequirements GetMobile()
- {
- return CellPhone.GetMobileRequirements();
- }
-
- public void AssembleMobile()
- {
- this.CellPhone.BuildModelName();
- this.CellPhone.BuildDisplayResolution();
- this.CellPhone.BuildProcessor();
- this.CellPhone.BuildRAM();
- this.CellPhone.BuildROM();
- }
- }
Last as usual, but not the least, the caller: class program.
- class Program
- {
- static void Main(string[] args)
- {
- IBuildMobile Apple = new Apple();
-
- Mobile mobile = new Mobile(onePlus);
-
- mobile.AssembleMobile();
- Console.WriteLine($" Model Name: {mobile.GetMobile().ModelName}\n Display resolution: {mobile.GetMobile().DisplayResolution} \n RAM: {mobile.GetMobile().RAM} GB\n ROM: {mobile.GetMobile().ROM} GB \n Processor: {mobile.GetMobile().Processor}");
-
- }
- }
Let's see the output when the client wants an Apple phone:
And when the client wants the Oneplus phone. Note: just uncomment above code and run the application:
Perfect & flawless!
Summary
In this article, we learned
- Where the builder pattern fits into the larger design pattern picture.
- How to create an object class and abstract out its constructor into an interface.
- How to create and configure concrete builder representations.
- The role of the director-class and managing the overall build process.
I hope you've come away from this with a real grasp of the builder pattern and how it can be applied to your personal projects in the future.
If you've enjoyed the article, which I hope you have, please apply your knowledge to enliven the code in your projects.
if you have any questions or just want to connect, follow these links.
You can download the project for your references. It's free to use/modify & it's in .net core.
As always, Happy coding!