Design patterns: Builder, Fluent Interface and classic builder

in #design-patterns6 years ago

build-460x300.jpg

In this lesson about the Builder design pattern, or rather two of its types, which apply to the way of creating a class object, and creating an object from other objects. More details in the article.

Description and implementation way

The goal of the builder is to separate the way the object is created from its representation. So the process of creating an object is divided into several parts.

First, I will show an example of a builder pattern on the basis of Fluent Interface, then a classic builder.

The Fluent Interface builder should implement when the constructor has more than four or five parameters, we create a builder class, inside this class, which has this constructor with these many parameters.

Examples in which Fluent Interface Builder would be applicable are everywhere where we have constructors that take many parameters, and Classic Builder, for example in software that accepts any input data, converting it and based on input data, creates output data, i.e. in some game where, under the influence of the user’s actions, the game performs specific events, the builder will be here the code that creates specific events in the game depending on the user’s choices.

Builder Fluent Interface

However, we will refer to our example of the Shop, there was such a Client class:

class Client
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string ZipCode { get; set; }
    private double _myWallet;
    public double MyWallet { get => _myWallet; set => _myWallet = value; }
 
    public Client(ClientConstructor myconfiguration)
    {
        FirstName = myconfiguration.firstname;
        LastName = myconfiguration.lastname;
        ZipCode = myconfiguration.zipcode;
        _myWallet = myconfiguration.myWallet;
    }
}
 
public class ClientConstructor
{
    public string firstname;
    public string lastname;
    public string zipcode;
    public double myWallet;
}

We are adding a few other variables such as customer’s address, street, house number and city:

class Client
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string ZipCode { get; set; }
    private double _myWallet;
    public double MyWallet { get=>_myWallet; set => _myWallet = value; }
    public string Town { get; set; }
    public string Street { get; set; }
    public int HomeNumber { get; set; }
 
    public Client(ClientConstructor myconfiguration)
    {
        FirstName = myconfiguration.firstname;
        LastName = myconfiguration.lastname;
        ZipCode = myconfiguration.zipcode;
        _myWallet = myconfiguration.myWallet;
        Town = myconfiguration.town;
        Street = myconfiguration.street;
        HomeNumber = myconfiguration.homenumber;
    }
}
 
public class ClientConstructor
{
    public string firstname;
    public string lastname;
    public string zipcode;
    public double myWallet;
    public string town;
    public string street;
    public int homenumber;
}

It does not look very clear, when we will use the fluent builder here, not only will it be much more readable, but we will also have a separate process of creating customer data from manipulating, representing them if we want to do something with this data later, so now we implement a fluent builder here the method shown below:

class Client
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string ZipCode { get; set; }
    private double _myWallet;
    public double MyWallet { get=>_myWallet; set => _myWallet = value; }
    public string Town { get; set; }
    public string Street { get; set; }
    public int HomeNumber { get; set; }
 
    public Client(Builder builder)
    {
        FirstName = builder.firstname;
        LastName = builder.lastname;
        ZipCode = builder.zipcode;
        _myWallet = builder.myWallet;
        Town = builder.town;
        Street = builder.street;
        HomeNumber = builder.homenumber;
    }
 
    public class Builder
    {
        public string firstname;
        public string lastname;
        public string zipcode;
        public double myWallet;
        public string town;
        public string street;
        public int homenumber;
 
        public Builder FirstName(string firstname)
        {
            this.firstname = firstname;
            return this;
        }
 
        public Builder LastName(string lastname)
        {
            this.lastname = lastname;
            return this;
        }
 
        public Builder ZipCode(string zipcode)
        {
            this.zipcode = zipcode;
            return this;
        }
 
        public Builder MyWallet(double myWallet)
        {
            this.myWallet = myWallet;
            return this;
        }
 
        public Builder Town(string town)
        {
            this.town = town;
            return this;
        }
 
        public Builder HomeNumber(int homenumber)
        {
            this.homenumber = homenumber;
            return this;
        }
 
        public Builder Street(string street)
        {
            this.street = street;
            return this;
        }
 
        public Client build()
        {
            return new Client(this);
        }
    }
}

As you can see, we have separated the saving of customer data from the rest of the logic, and we can control to a greater extent how the object is created.

Also saving data in the “Main” function is much more readable:

static void Main(string[] args)
{
    Client client = new Client.Builder()
        .FirstName("Slawomir")
        .LastName("Kowalski")
        .ZipCode("34 174")
        .MyWallet(456.32)
        .HomeNumber(23)
        .Town("Cambridge")
        .Street("botolph lane")
        .build();
 
    Console.WriteLine("Data client: "+"\nFirstName: " + client.FirstName+
        "\nKowalski: " +client.LastName +
        "\nZip code: " + client.ZipCode +
        "\nWallet: " + client.MyWallet +
        "\nHome number: " + client.HomeNumber +
        "\nTown: " + client.Town +
        "\nStreet: "+client.Street);
 
        Console.ReadKey();
}

Now you can see what data is saved to the object.

Result:

fluentinterfaceimgpng.png

This is the Builder Fluent Interface, now we’ll do the example of a classic builder.

Classic Builder

Structure

First, let’s see what the UML Builder diagram looks like:

builderstructure.png

Converter class as you can see, creates instances of individual classes that read different data formats, and here the Reader class is a client who only reads these formats.

An example of a builder can be eg a customer who orders food from a restaurant, look at the picture below:

hotdogs.png

First, the customer orders a meal, then the report comes to the manager, who then tells the employees who later execute the order together with the delivery to the house. In the code, we will make an example of our store.

Example

Let’s build our store, let’s separate its objects, eg in this way walls, roof, floor, what will it look like in a classic builder?

An example may start a fright at the beginning, but it’s really a simple pattern, you just have to convert it into practice.

I will translate pieces of the whole code one by one, I will give the whole example at the end of the lesson in the source files, because it is long.

The classic builder can be treated as a plan.

The diagram of the created store looks like this:

builder.png

We add to the Shop class, roof, floor, and wall, then create a store object with the manager in the client, in our case in the Main function, the whole is designed so that the customer can’t see how the store is created, client is commissioning the build shop the builder so relating to our example, Director class, and the client does not care how the store is built, only the finished product is delivered to him.

Let’s see now how it looks in the code, let’s start from the left side of the diagram, ie the Shop, Roof, Floor, Wall classes:

Shop:

namespace BuilderShop
{
    public class Shop
    {
        private IRoof roof;
 
        private IFloor floor;
 
        private IWall wall;
 
        public void SetRoof(IRoof roof)
        {
            this.roof = roof;
        }
 
        public IRoof GetRoof()
        {
            return roof;
        }
 
        public void SetFloor(IFloor floor)
        {
            this.floor = floor;
        }
 
        public IFloor GetFloor()
        {
            return floor;
        }
 
        public void SetWall(IWall wall)
        {
            this.wall = wall;
        }
 
        public IWall GetWall()
        {
            return wall;
        }
    }
}

Floor:

namespace BuilderShop
{
    public interface IFloor
    {
        int Lenght { get; set; }
        int NumberOfTiles { get; set; }
        string TypeGreatnessTiles { get; set; }
 
        int ReturnLenght();
        int ReturnNumberOfTiles();
        string ReturnTypeGreatnessTiles();
 
        void SetLenght(int Lenght);
        void SetNumberOfTiles(int NumberOfTiles);
        void SetTypeGreatnessTiles(string TypeGreatnessTiles);
    }
 
    public class Floor : IFloor
    {
        public int Lenght { get; set; }
        public int NumberOfTiles { get; set; }
        public string TypeGreatnessTiles { get; set; }
 
        public int ReturnLenght()
        {
            return Lenght;
        }
        public int ReturnNumberOfTiles()
        {
            return NumberOfTiles;
        }
        public string ReturnTypeGreatnessTiles()
        {
            return TypeGreatnessTiles;
        }
 
        public void SetLenght(int Lenght)
        {
            this.Lenght = Lenght;
        }
        public void SetNumberOfTiles(int NumberOfTiles)
        {
            this.NumberOfTiles = NumberOfTiles;
        }
        public void SetTypeGreatnessTiles(string TypeGreatnessTiles)
        {
            this.TypeGreatnessTiles = TypeGreatnessTiles;
        }
    }
}

Roof:

namespace BuilderShop
{
    public interface IRoof
    {
        int Height { get; set; }
 
        int ReturnHeight();
 
        void SetHeight(int Height);
    }
 
    public class Roof : IRoof
    {
        public int Height { get; set; }
 
        public int ReturnHeight()
        {
            return Height;
        }
 
        public void SetHeight(int Height)
        {
            this.Height = Height;
        }
    }
}

Wall:

namespace BuilderShop
{
    public interface IWall
    {
        int Thickness { get; set; }
        string Color { get; set; }
 
        int ReturnThickness();
        string ReturnColor();
 
        void SetThickness(int Thickness);
        void SetColor(string Color);
    }
 
    public class Wall : IWall
    {
        public int Thickness { get; set; }
        public string Color { get; set; }
 
        public int ReturnThickness()
        {
            return Thickness;
        }
 
        public string ReturnColor()
        {
            return Color;
        }
 
        public void SetThickness(int Thickness)
        {
            this.Thickness = Thickness;
        }
 
        public void SetColor(string Color)
        {
            this.Color = Color;
        }
    }
}

We implement its elements in the shop class, but in the form of interfaces, we stick to the fifth SOLID principle, dependency inversion, class relations should result from abstraction and high-level modules should not depend on low-level modules, the store is a high-level module and the roof, floor, wall they are low-level modules, such a small reminder on the SOLID principles 🙂

Our builder looks like this:

public interface IShopBuilder
{
    void BuildFloor();
    void BuildRoof();
    void BuildWall();
    Shop getShop();
}

Is an interface that we implement to the store class we want to build and we want to build a large Tesco store 🙂

It looks like this:

namespace BuilderShop
{
    class BigShopTesco : IShopBuilder
    {
        private Shop shop;
 
        public BigShopTesco()
        {
            this.shop = new Shop();
        }
 
        public void BuildFloor()
        {
            IFloor floor = new Floor();
 
            floor.SetLenght(20);
            floor.SetNumberOfTiles(100);
            floor.SetTypeGreatnessTiles("Big");
 
            shop.SetFloor(floor);
        }
 
        public void BuildRoof()
        {
            IRoof roof = new Roof();
 
            roof.SetHeight(10);
 
            shop.SetRoof(roof);
        }
 
        public void BuildWall()
        {
            IWall wall = new Wall();
 
            wall.SetThickness(1);
            wall.SetColor("Green");
 
            shop.SetWall(wall);
        }
 
        public Shop getShop()
        {
            return shop;
        }
    }
}

We set in methods the BigShopTesco class parameters sets its elements and write them to the interfaces of the Shop class.

We call the BigShopTesco class methods in our manager, in the ShopDirector class:

namespace BuilderShop
{
    public class ShopDirector
    {
        private IShopBuilder shopbuilder;
 
        public ShopDirector(IShopBuilder shopbuilder)
        {
            this.shopbuilder = shopbuilder;
        }
 
        public void buildShop()
        {
            shopbuilder.BuildFloor();
            shopbuilder.BuildRoof();
            shopbuilder.BuildWall();
        }
 
        public Shop getShop()
        {
            return shopbuilder.getShop();
        }
    }
}

To the constructor of the ShopDirector class we pass the object of the class that we want to create, that is BigShopTesco and we call its methods.

And from the client’s side it looks like this:

namespace BuilderShop
{
    class Program
    {
        static void Main(string[] args)
        {
            IShopBuilder shopbuilder = new BigShopTesco();
            ShopDirector shopdirector = new ShopDirector(shopbuilder);
            shopdirector.buildShop();
 
            Shop shop = shopdirector.getShop();
            Console.WriteLine("Shop color: " + shop.GetWall().ReturnColor() +
               "\nThickness of the shop wall: " + shop.GetWall().ReturnGage() +
               "\nThe height of the roof: " + shop.GetRoof().ReturnHeight() +
               "\nNumber of tiles: " + shop.GetFloor().ReturnNumberOfTiles() +
               "\nThe length of the floor: " + shop.GetFloor().ReturnLenght() +
               "\nThe type of tile size: " + shop.GetFloor().ReturnTypeGreatnessTiles());
 
            Console.ReadKey();
        }
    }
}

As you can see, the customer does not see how the store is built, and so it should be him does not care about it, he just orders the manager.

At the end of the lesson I will give the source code to this builder.

Remember, nonsense is just a read, to understand it is best to create your own builder, you can help yourself that what I did to make my own builder. You will not understand it without practice.

In both cases you should use Builder wisely, because when we have a lot of these builders, the code becomes unreadable.

Result:

BuilderExample.png

Advantages and disadvantages

Advantages

  1. High scalability (creating new data is easier)
  2. More control over how the object is created (The process of creating the object is independent of its other representations)
  3. Better readability (with proper use 🙂)
  4. Large diversity of class interiors.

Disadvantages

  1. A large number of objects
  2. Improper use of this pattern may cause the code to be unreadable (one object may contain too many builders)

Relations with other patterns

  1. Builder is often used with the Bridge pattern, Director is an interface and the builder is in the implementation role.
  2. The builder is used with the Composite pattern to create a tree.
  3. Patterns The Abstract Factory, Builder and Prototype can be implemented as Singletons
  4. Often, the Builder’s implementation is combined with an Abstract factory to maintain flexibility and not create concrete types of classes.

Summary

This long lesson came out, but it’s probably good because the builder was hopefully well explained.

That’s all about Builder.

Link to github with the whole code from this article: https://github.com/Slaw145/BuilderTutorial

This content also you can find on my blog http://devman.pl/programtech/design-patterns-builder-fluent-interface-classic-builder/

If you recognise it as useful, share it with others so that others can also use it.

Leave upvote and follow and wait for next articles :) .

In the next article, we will talk about the Abstract Factory pattern.

And NECESSERILY join the DevmanCommunity community on fb, part of the community is in one place 🙂

– site on fb: Devman.pl-Sławomir Kowalski

– group on fb: DevmanCommunity

Ask, comment underneath at the end of the post, share it, rate it, whatever you want🙂.

Coin Marketplace

STEEM 0.26
TRX 0.11
JST 0.033
BTC 64266.94
ETH 3077.24
USDT 1.00
SBD 3.87