Ankit Rana Ankit Rana - 28 days ago 14
C# Question

Nested menu builder never end properly in c#

Here I am building menu builder with n number of leaf and branch,Now I am impotent with a problem by not properly return the reference of a builder.

Here is code

public interface IMenuBuilder
{
IMenuBuilder AddLeaf(string name);
ISubMenuBuilder AddBranch(string name);
void Build();
}

public interface ISubMenuBuilder : IMenuBuilder
{
ISubMenuBuilder AddLeaf(string name);
INestSubMenuBuilder AddBranch(string name);
IMenuBuilder Build();
}

public interface INestSubMenuBuilder : ISubMenuBuilder
{
INestSubMenuBuilder AddLeaf(string name);
INestSubMenuBuilder AddBranch(string name);
ISubMenuBuilder Build();
}

public class MenuBuilder : IMenuBuilder
{
List<Menu> menus = new List<Menu>();
public IMenuBuilder AddLeaf(string name)
{
var menu = new RootMenu { Name = name };
menus.Add(menu);
return this;
}

public ISubMenuBuilder AddBranch(string name)
{
var menu = new RootMenu { Name = name };
menus.Add(menu);
return new SubMenuBuilder(this, menu);
}

public void Build()
{
}
}

public class SubMenuBuilder : ISubMenuBuilder
{
private IMenuBuilder menuBuilder;
private ISubMenuHolder menu;

public SubMenuBuilder(IMenuBuilder menuBuilder, ISubMenuHolder menu)
{
this.menuBuilder = menuBuilder;
this.menu = menu;
}

protected SubMenuBuilder(ISubMenuHolder menu)
{
this.menu = menu;
}

public ISubMenuBuilder AddLeaf(string name)
{
var leafMenu = new LeafMenu { Name = name };
leafMenu.ParentId = (leafMenu as Menu).Id;
menu.AddSubMenu(leafMenu);
return this;
}

public INestSubMenuBuilder AddBranch(string name)
{
var branchMenu = new BranchMenu { Name = name };
branchMenu.ParentId = (branchMenu as Menu).Id;
menu.AddSubMenu(branchMenu);
return new NestSubMenuBuilder(this, branchMenu);
}

public IMenuBuilder Build()
{
return menuBuilder;
}


IMenuBuilder IMenuBuilder.AddLeaf(string name)
{
this.AddLeaf(name);
return this;
}

ISubMenuBuilder IMenuBuilder.AddBranch(string name)
{
this.AddBranch(name);
return this;
}

void IMenuBuilder.Build()
{
}
}

public class NestSubMenuBuilder : INestSubMenuBuilder
{
private ISubMenuBuilder subMenuBuilder;
private ISubMenuHolder branchMenu;

public NestSubMenuBuilder(ISubMenuBuilder subMenuBuilder, ISubMenuHolder branchMenu)
{
this.subMenuBuilder = subMenuBuilder;
this.branchMenu = branchMenu;
}

public INestSubMenuBuilder AddLeaf(string name)
{
var leafMenu = new LeafMenu { Name = name };
leafMenu.ParentId = (leafMenu as Menu).Id;
branchMenu.AddSubMenu(leafMenu);
return this;
}

public ISubMenuBuilder Build()
{
return subMenuBuilder;
}

public INestSubMenuBuilder AddBranch(string name)
{
var menu = new BranchMenu { Name = name };
menu.ParentId = (branchMenu as Menu).Id;
branchMenu.AddSubMenu(menu);
return new NestSubMenuBuilder(this, menu);
}

ISubMenuBuilder ISubMenuBuilder.AddLeaf(string name)
{
this.AddLeaf(name);
return this;
}

IMenuBuilder ISubMenuBuilder.Build()
{
return subMenuBuilder;
}

IMenuBuilder IMenuBuilder.AddLeaf(string name)
{
this.AddLeaf(name);
return this;
}

ISubMenuBuilder IMenuBuilder.AddBranch(string name)
{
this.AddBranch(name);
return this;
}

void IMenuBuilder.Build()
{
}
}


this is my builder's code and here is an example where I got issue while using this builder API.

builder
.AddLeaf("Leaf 4")
.AddBranch("Branch 2") // subMenuBuilder
.AddLeaf("Branch 2 -> Leaf 1")
.AddBranch("Branch 2 -> Branch 1") //nestSubMenuBuilder 1
.AddLeaf("Branch 2 -> Branch 1 -> Leaf 3")
.AddBranch("Branch 2 -> Branch 1 -> Branch 1") //nestSubMenuBuilder 2
.AddLeaf("Branch 2 -> Branch 1 -> Branch 1 -> Leaf 1")
.Build() // nestSubMenuBuilder 1
.AddLeaf("Branch 2 -> Leaf 4")
.Build() // subMenuBuilder
.AddLeaf("Branch 2 -> Leaf 2")
.Build();// menubuilder


here I am not getting last menu builder reference back to add new leaf and branch.

Answer

Your problem is, that your code returns a different builder object for each menu level. Instead, you should always return the same kind of builder and when the top level is reached, just return null from Build()

Proposed builder code:

public interface IMenuBuilder
{
    IMenuBuilder AddLeaf(string name);
    IMenuBuilder AddBranch(string name);
    IMenuBuilder Build();
}

public class MenuBuilder : IMenuBuilder
{
    private IMenuBuilder menuBuilder;
    private ISubMenuHolder menu;

    /// <summary>
    /// Create a new menu with specified root item
    /// </summary>
    public MenuBuilder(string rootName)
    {
        this.menuBuilder = null;
        this.menu = new RootMenu { Name = rootName };
    }

    /// <summary>
    /// Create a new sub menu with specified parents
    /// </summary>
    public MenuBuilder(IMenuBuilder parentBuilder, ISubMenuHolder menu)
    {
        this.menuBuilder = parentBuilder;
        this.menu = menu;
    }

    public IMenuBuilder AddLeaf(string name)
    {
        var leafMenu = new LeafMenu { Name = name };
        leafMenu.ParentId = menu.Id;
        menu.AddSubMenu(leafMenu);
        return this;
    }

    public IMenuBuilder AddBranch(string name)
    {
        var branchMenu = new BranchMenu { Name = name };
        branchMenu.ParentId = menu.Id;
        menu.AddSubMenu(branchMenu);
        return new MenuBuilder(this, branchMenu);
    }

    public IMenuBuilder Build()
    {
        return menuBuilder;
    }
}
Comments