Search code examples
c#apibuilderfluent

How to correctly tailor a fluent builder


I'm creating a helper class which should guide my co-workers in using it. Let's assume its a string-builder for proper building of a markdown-string.

string result = new OwnStringBuilder()
            .NewTable()
                .Tablehead("head1")
                .Tablehead("head2")
                .TableheadEnding()
                .Tablecontent("content1")
                .Tablecontent("content2")
                .TableNextLine()
                .Tablecontent("content3")
                .Tablecontent("content4")
            .EndTable()

It works properly as intended but I want to restrict the possibilities of the user. When typing .NewTable. it shows all possible methods like Tablehead, TableheadEnding, Tablecontent, TableNextLine and EndTable. I hope there is a way to restrict the table to use just the method tablehead. The Tablehead just to use either another Tablehead or the TableheadEnding and so on.

The table itself is created via

public OwnStringBuilder NewTable()
    {
        return new OwnStringBuilder(this, StringBuilder.AppendLine());
    }

and e.g. the Tablehead via

public OwnStringBuilderTableHelper Tablehead(string content)
    {
        StringBuilder.Append(content);
        return this;
    }

I've already googled but it's already rare to get good examples for this.


Solution

  • You can get it using Interfaces.

    Each method only shows and allows the methods that are declared in the interface.

    // only Head() function is allowed
    internal interface ITable
    {
        ITableHead Head(string value);
    }
    
    // it allows Head(), Content() and End() functions.
    internal interface ITableHead
    {
        ITableHead Head(string value);
        ITableHead Content(string value);
        ITable End();
    }
    

    This is your table class, that implements all interfaces:

    internal class MyTable : ITable, ITableHead
    {
        public MyTable() { }
    
        public ITableHead Head(string value)
        {
            // add value
            return (ITableHead)this;
        }
    
        public ITableHead Content(string value)
        {
            // add value
            return (ITableHead)this;
        }
    
        public ITable End()
        {
            // some op
            return this;
        }
    }
    

    And that's the builder:

    internal class MyTableBuilder 
    {
        private MyTable _myTable;
    
        public static ITable CreateTable()
        {
            return new MyTable();
        }
    
        public ITableHead Head(string value)
        {
            // add data
            return _myTable.Head(value);
        }
    
        public ITableHead Content(string value)
        {
            // add data
            return _myTable.Content(value);
        }
    
        public ITable End()
        {
            return _myTable.End();
        }
    }
    

    Now you can build a class in that way:

    var t = MyTableBuilder.CreateTable()
                .Head("head1")
                .Head("head2")
                .Content("content")
                .End();
    

    You could even hide MyTable declaring it inside the builder: