DEV Community

Cover image for Builder design pattern for objects with nested properties
Ali Alp
Ali Alp

Posted on • Edited on

9

Builder design pattern for objects with nested properties

A typical builder design pattern is suitable for a one-dimensional domain object like this

public class Data
{
    public string Id { get; set; }
    public DateTime DateTime { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

and its corresponding builder class will be as follows

public class DataBuilder
{
    private readonly Data _data;

    public DataBuilder()
    {
        _data = new Data();
    }

    public DataBuilder Id(string id)
    {
        this._data.Id = id;
        return this;
    }

    public DataBuilder Datetime(DateTime dateTime)
    {
        this._data.DateTime = dateTime; 
        return this;
    }

    public string Build()
    {
        return $"id: {_data.Id}  date:{_data.DateTime}";
    }
}
Enter fullscreen mode Exit fullscreen mode

so it can be used like this

var result = new DataBuilder()
            .Id("11")
            .Datetime(DateTime.Now)
            .Build();
Enter fullscreen mode Exit fullscreen mode

but what if our domain object consist of other domain objects like this

public class Data
{
    public string Id { get; set; }
    public DateTime DateTime { get; set; }
    public Nested Nested { get; set; }
}

public class Nested
{
    public string Name { get; set; }
    public string Surname { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

in this case the beauty of the usage of the builder pattern will be somehow compromised

var nested = new Nested()
{
    Name="Ali",
    Surname="Tabryzy"
};

var result = new DataBuilder()
    .Id("11")
    .Nested(nested)
    .Datetime(DateTime.Now)
    .Build();
Enter fullscreen mode Exit fullscreen mode

In order to keep the beauty of the builder pattern we can implement it like this

public class DataBuilder
{
    private readonly Data _data;
    public readonly NestedBuilder Nested;
    private Nested _nested;

    public class NestedBuilder
    {
        private readonly DataBuilder _dataBuilder;

        public NestedBuilder(DataBuilder dataBuilder)
        {
            _dataBuilder = dataBuilder;
        }

        public DataBuilder Name(string name)
        {
            if (_dataBuilder._nested == null)
                _dataBuilder._nested = new Nested();
            _dataBuilder._nested.Name = name;
            return _dataBuilder;
        }

        public DataBuilder Surname(string surname)
        {
            if (_dataBuilder._nested == null)
                _dataBuilder._nested = new Nested();
            _dataBuilder._nested.Surname = surname;
            return _dataBuilder;
        }
    }

    public DataBuilder()
    {
        _data = new Data();
        Nested = new NestedBuilder(this);
    }


    public DataBuilder Datetime(DateTime dateTime)
    {
        this._data.DateTime = dateTime; 
        return this;
    }
    public DataBuilder Id(string id)
    {
        this._data.Id = id;
        return this;
    }

    public string Build()
    {
        return $"id: {_data.Id} name:" + 
            $"{_nested.Name} surname: {_nested.Surname} date:{_data.DateTime}";
    }
}
Enter fullscreen mode Exit fullscreen mode

which the usage will be as beautiful as it is for a one-dimensional domain object as follows

var result = new DataBuilder()
    .Id("11")
    .Nested.Name("ali")
    .Nested.Surname("Tabryzy")
    .Datetime(DateTime.Now)
    .Build();
Enter fullscreen mode Exit fullscreen mode

Happy coding :)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (2)

Collapse
 
rajajaganathan profile image
Raja Jaganathan • Edited

Mostly I use build pattern to construct payload for third party or api. By the way it's really usefully!

Collapse
 
chrisribe profile image
ChrisRibe

Thanks for the article, very useful :)
Here is a reimplementation in Javascript / Typescript.

codesandbox.io/s/builder-pattern-n...

Eliminate Context Switching and Maximize Productivity

Pieces.app

Pieces Copilot is your personalized workflow assistant, working alongside your favorite apps. Ask questions about entire repositories, generate contextualized code, save and reuse useful snippets, and streamline your development process.

Learn more