Search code examples
c#design-patternsbuilder-pattern

Implementing Fluent Builder Pattern for nested clases


I am trying to understand fluent builder patterns and in my case, I have an object that has child objects too. Most of the online examples are based on a single object. There are some examples for nested objects but they all are different. So I am here to find out what is the correct way to implement a fluent builder pattern for my case.

public class Controller
{
  private Robot _robot;
  private Table _table;              
  public Controller() {}
  public void Place() {}
  public void Action() {}
}

public class Robot
{
  private Point _currentLocation;
  public Robot(Point point) {}
  public void Move(){}
}

public class Table
{
  private int _width;
  private int _length;
  public Table(int width, int length){}
  public bool IsValidLocation(Point point){}
}

------------ Edited ----------

I have approached implementing this as below. Any expert advice on the below approach?

public class TableBuilder
{
    private readonly Table _table = new();

    public Table Build() => _table;

    public TableBuilder WithWidth(int width)
    {
        _table.Width = width;
        return this;
    }

    public TableBuilder WithLength(int length)
    {
        _table.Length = length;
        return this;
    }
} 

public class RobotBuilder
{
    private readonly Robot _robot = new();

    public Robot Build() => _robot;

    public RobotBuilder WithLocation(Point point)
    {
        _robot.CurrentLocation = point;
        return this;
    }
}

public class ControllerBuilder
{
    private readonly Controller _controller = new();
    private readonly TableBuilder _tableBuilder = new();
    private readonly RobotBuilder _robotBuilder = new();


    public Controller Build() => _controller;

    public ControllerBuilder WithTable(int width, int length)
    {
        _controller.Table = _tableBuilder.WithWidth(width)
                                        .WithLength(length)
                                        .Build();
        return this;
    }

    public ControllerBuilder WithRobot(Point point)
    {
        _controller.Robot = _robotBuilder.WithLocation(point)
                                        .Build();
        return this;
    }
}

Finally I used below code to declare the Controller

var _controller = new ControllerBuilder()
                    .WithRobot(new Point(x, y))
                    .WithTable(5, 5)
                    .Build();

Solution

  • What I usually do in this cases is to use nested builders with lambdas, which are useful when the nested classes have many parameters needed at construction time:

    public class Controller
    {
        private readonly Robot _robot;
        private readonly Table _table;
    
        public Controller(Robot robot, Table table)
        {
            _robot = robot;
            _table = table;
        }
    
        public void Place()
        {
        }
    
        public void Action()
        {
        }
    }
    
    public class Robot
    {
        private Point _currentLocation;
    
        public Robot(Point point)
        {
        }
    
        public void Move()
        {
        }
    }
    
    public class Table
    {
        private readonly int _width;
        private readonly int _length;
    
        public Table(int width, int length)
        {
            _width = width;
            _length = length;
        }
    
    
        public bool IsValidLocation(Point point)
        {
            return false;
        }
    }
    
    public class TableBuilder
    {
        private int _width;
        private int _length;
        public TableBuilder WithWidth(int width)
        {
            _width = width;
            return this;
        }
    
        public TableBuilder WithLength(int length)
        {
            _length = length;
            return this;
        }
    
        public Table Build() => new Table(_width, _length);
    }
    
    public class RobotBuilder
    {
        private Point _location;
    
        public Robot Build() => new Robot(_location);
    
        public RobotBuilder WithLocation(Point location)
        {
            _location = location;
            return this;
        }
    }
    
    public class ControllerBuilder
    {
        private Robot _robot;
        private Table _table;
    
    
        public Controller Build() => new Controller(_robot, _table);
    
        public ControllerBuilder WithRobot(Func<RobotBuilder, RobotBuilder> builderDirector)
        {
            _robot = builderDirector.Invoke(new RobotBuilder()).Build();
            return this;
        }
    
        public ControllerBuilder WithTable(Func<TableBuilder, TableBuilder> builderDirector)
        {
            _table = builderDirector(new TableBuilder()).Build();
            return this;
        }
    }
    
    public class Program
    {
        public static void Main()
        {
            var controller = new ControllerBuilder()
                .WithTable(builder => builder.WithLength(1).WithWidth(2))
                .WithRobot(builder => builder.WithLocation(new Point(1,1)))
                .Build();
        }
    }