DEV Community

loading...
Cover image for Builder design pattern for objects with nested properties

Builder design pattern for objects with nested properties

Ali Alp
“Take it easy” is nonsense , take it as hard as you can and don’t let it go :)
Updated on ・2 min read

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 :)

Discussion (1)

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!