Fluent hierarchical construction

Building a hierarchy of objects doesn’t happen that often in code, but when it does, it can get pretty ugly.  Most of the time, the hierarchy will come out of the database.  Recently, we had a hierarchy that needed to be built straight in code.  We had something like this going on:

public static Menu BuildMenu()
{
    var menu = new Menu();

    // Main menu
    var products = new MenuItem();
    products.Title = "Products";

    var services = new MenuItem();
    services.Title = "Services";

    var support = new MenuItem();
    support.Title = "Support";

    var contact = new MenuItem();
    contact.Title = "Contact";

    menu.AddItem(products);
    menu.AddItem(services);
    menu.AddItem(support);
    menu.AddItem(contact);

    // Products menu
    var hardware = new MenuItem();
    hardware.Title = "Hardware";

    var software = new MenuItem();
    software.Title = "Software";

    products.AddItem(hardware);
    products.AddItem(software);

    // Hardware menu
    var computers = new MenuItem();
    computers.Title = "Computers";

    var printers = new MenuItem();
    printers.Title = "Printers";

    hardware.AddItem(computers);
    hardware.AddItem(printers);

    // Services menu
    var consulting = new MenuItem();
    consulting.Title = "Consulting";

    var onSiteSupport = new MenuItem();
    onSiteSupport.Title = "On-Site Support";

    services.AddItem(consulting);
    services.AddItem(onSiteSupport);

    return menu;
}

Our example was different, but it’s the same idea of building a hierarchy of objects.  In the above code, we’re trying to build out our menu structure for a website.  This is a simple hierarchy, but more complex ones could go on for hundreds of lines.  It’s almost impossible to see what’s going on there, and what is a child of what.  You can try and create conventions inside the code, such as the comments and organization of the building.

There are some more fluent ways to build hierarchies out there, as shown by LINQ to XML.  Instead of manually setting up all of the objects, we use one statement to construct everything, making use of existing features around since .NET 1.0.

The basic idea behind fluent hierarchical construction is:

  • Create a constructor that takes all necessary properties to set up the individual item
  • Add a params parameter that takes all the children for the parent

First, we can switch our Menu class to use fluent construction:

public class Menu
{
    private readonly List<MenuItem> _menuItems = new List<MenuItem>();

    public Menu(params MenuItem[] menuItems)
    {
        _menuItems.AddRange(menuItems);
    }

    public void AddItem(MenuItem menuItem)
    {
        _menuItems.Add(menuItem);
    }

    public IEnumerable<MenuItem> GetMenuItems()
    {
        return _menuItems;
    }
}

We created a constructor that took a param array of child menu items, which are then added to its menu items collection.  Next, we need to switch the child type to take both the Title as well as the child menu items:

public class MenuItem
{
    private readonly List<MenuItem> _menuItems = new List<MenuItem>();

    public MenuItem(string title, params MenuItem[] menuItems)
    {
        Title = title;
        _menuItems.AddRange(menuItems);
    }

    public string Title { get; set; }

    public void AddItem(MenuItem menuItem)
    {
        _menuItems.Add(menuItem);
    }

    public IEnumerable<MenuItem> GetMenuItems()
    {
        return _menuItems;
    }
}

All properties that we need to set on a hierarchy class need to go in the constructor for this technique to work.  Optional or additional parameters can be added with overloaded constructors, with the additional parameters added just before the params one.

With our new constructors in place, our menu construction becomes quite a bit terser:

public static Menu BuildMenu()
{
    var menu =
        new Menu(
            new MenuItem("Products",
                new MenuItem("Hardware",
                    new MenuItem("Computers"),
                    new MenuItem("Printers")),
                new MenuItem("Software")),
            new MenuItem("Services",
                new MenuItem("Consulting"),
                new MenuItem("On-Site Support")),
            new MenuItem("Support"),
            new MenuItem("Contact")
        );

    return menu;
}

Not only is the number of lines of code reduced, but I can far more easily discern the structure of the hierarchy just by looking at the structure of the construction.  You can literally see the tree of objects in the construction.  No more temporary variables hang around, just so we can add children with individual method calls.  Because we used the params keyword, we don’t even need to provide any children if they don’t exist.  Params also allow us to create a comma-separated list of the children, instead of creating an array.

The nice thing is, all this technique used was application of the params keyword in the constructor for child objects.  As long as we ensure that all properties needed to be set are available in an overloaded constructor, this technique should work.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Kyle

    Hmm, interesting. I had written an XML file builder that worked in exactly the same way just for fun, and fun it was! I implemented it in terms of the decorator pattern – when you added a new child node, you put it into the ctor of the parent, and so on. It ended up being fairly difficult to make attributes and such, but your format is better for something more stable such as menu-building.

    However, I don’t really see how your final piece of code can be considered “fluent”; it’s just a series of news! That’s okay though, I still really like the way this works.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Kyle

    Yeah, I don’t really know what the definition of “Fluent” might be…”code that looks pretty”?

  • http://www.gridviewguy.com Mohammad Azam

    Hi,

    I really like the way you have indented the Menu creation code. For me that is very readable and self explanatory.

    Thanks,
    Azam

  • http://scottic.us Scott Bellware

    I’d have made methods called ‘Menu’ and ‘MenuItem’ that are builders and gotten rid of the ‘new’ noise. Within the local scope of builder code, opting for declarative syntax over imperative syntax (BuildMenuItem, etc), is consistent with language-orientation even if it bends the style guide for the language.

  • http://www.spinthemoose.com/ David Alpert

    With “fluent” in your title, i expected something more like the following:

    public static Menu BuildMenu()
    {
    var menu =
    new Menu().
    WithSubMenu(“Products”).
    WithSubMenu(“Hardware”).
    WithItem(“Computers”).
    WithItem(“Printers”).
    End().
    WithItem(“Software”).
    End().
    WithSubMenu(“Services”).
    WithItem(“Consulting”).
    WithItem(“On-Site Support”).
    End().
    WithItem(“Support”).
    WithItem(“Contact”);

    return menu;
    }

    The examples tagged with “fluent” that i have seen floating around the blogosphere all seem to expose an API that leans on chaining (returning some helper object), reads like English, and provides contextual intellisense as you build your expression.

    In the above example, i’d probably execute as an extension method on MenuItem that operates on it’s collection and returns the updated MenuItem. Hmmm, how to handle the submenu chaining? perhaps returning an intermediate helper object to ensure that the return type would notify you if you had forgotten an End()

  • http://scottic.us Scott Bellware

    David,

    Why does “With” need to prefix what are essentially operations that describe structure? That stuff is mostly noise.

    I think it’s valuable to question when building fluent interfaces whether we are cognizant of whether we’re trying to create domain-specific languages, or English. if we’re trying to create English, and we believe that English is what makes program code readable, we’re likely off-target.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    I think I still like Scott’s suggestion the best. The idea was to have a very readable building of a hierarchy. Method chaining has its place, but any time you have “End”, it’s an indication that chaining isn’t appropriate. Really, it’s the “params” keyword that enables a hierarchical code structure/object model.

  • Different view

    Sorry, I’m not a big fan of this code.

    Yes it’s readable, however it has very few benefits over simply writing straight HTML… you’re procedurally defining the options and there is no persistent queryable structure to build on in the future.

    To create a re-usable menu structure of arbitrary depth, I usually pull it from a table like this (MySQL semantics but you get the drift):

    create table menu_items(
    id int unsigned not null auto_increment primary key,
    parent_id int unsigned
    );

    alter table menu_items add foreign key(parent_id) references menu_items(id) on delete set null on update cascade;

    Then create a different table to hold titles, which may potentially be multilingual…

    create table menu_item_names(
    id int unsigned not null auto_increment primary key,
    item int unsigned not null,
    name tinytext not null,
    language int unsigned not null
    );

    alter table menu_item_names add foreign key(language) references languages(id) on delete cascade on update cascade;
    alter table menu_item_names add foreign key(item) references menu_items(id) on delete cascade on update cascade;

    Just a different way to accomplish the same thing … but more extensible / persistent (ie: tightly database-coupled, and therefore easier to build a simple interface for and delegate management to users…).

    It’s partly a different outlook – to me, these days, one-off code is just too hard to maintain, I tend to put the extra time in up front for peace of mind and re-usability.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Different view

    Yes, this would _usually_ be put in the database. But there are cases where I need to define hierarchies that shouldn’t be in the database. This really wasn’t about menu items, rather building hierarchies.

    In our case, each menu item represents new code. Since code and menus are tightly coupled, no point in putting them in the DB, as it would be more work to re-correlate them with specific view pages/controller actions.

  • Jacob Stanley

    Another option would be using a collection initializer:

    public static MenuItem BuildMenu()
    {
    var menu = new Menu()
    {
    new MenuItem(“Products”)
    {
    new MenuItem(“Hardware”)
    {
    new MenuItem(“Computers”),
    new MenuItem(“Printers”)
    },
    new MenuItem(“Software”)
    },
    new MenuItem(“Services”)
    {
    new MenuItem(“Consulting”),
    new MenuItem(“On-Site Support”)
    },
    new MenuItem(“Support”),
    new MenuItem(“Contact”)
    };

    return menu;
    }

    You’d just need to change your AddItem method to Add, and make sure the class supports IEnumerable:

    public class MenuItem : IEnumerable
    {
    private readonly List
    _menuItems = new List ();

    public MenuItem(string title)
    {
    Title = title;
    }

    public string Title
    {
    get;
    set;
    }

    public void Add(MenuItem menuItem)
    {
    _menuItems.Add(menuItem);
    }

    public IEnumerator GetEnumerator()
    {
    return _menuItems.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
    return GetEnumerator();
    }
    }

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Jacob

    Collection initializer is definitely another option. Do you find it easier to read? Personally, the brackets seem to add more distractions than purely constructors/builder methods.

  • http://www.nuba-group.com Fernando Arámburu

    I think David Alpert comment is the right one. He thought about building a menu as the builder design pattern said.
    “Different View” point of view sounds to me very strange as why do you need a DataBase to create objects? you just need object and their big potencial to build another objects.
    By the way , and just as we are talking about Constructor and as we are also talking about object oriented paradigm, I would say we should always do one thing you mention: “Create a constructor that takes all necessary properties to set up the individual item”. That’s the important thing. Imagine you create a Menu without a title … what does this menu means? If you make just one Menu constructor with its name you will always get Menu instances that means something…
    Another example of good use of Constructor would be a constructor for Persons.. Imagine a Person class and empty constructor… a person without name, age, sex? that wouldn’t be a person. A person should be instanciated with Name, Age, Sex and all other staff you need to have on the next statement a meaning Person.

    My two object oriented cents :)

  • Marti Kaljuve

    This type of chaining should also work:

    public static Menu BuildMenu(){    var menu = new Menu() .AddItem(“Products”, new Menu() .AddItem(“Hardware”, new Menu() .AddItem(“Computers”) .AddItem(“Printers”)) .AddItem(“Software”)) .AddItem(“Services”, new Menu() .AddItem(“Consulting”) .AddItem(“On-Site Support”)) .AddItem(“Support”) .AddItem(“Contact”);    return menu;}

    It’s basically the same as the original solution, but instead of params MenuItem[] menuItems in the constructor, there is an overload for AddItem that takes a single Menu object as the last parameter in case of a submenu.

    The indentation makes it easy to read, but the new Menu() initializers add some noise.

  • Marti Kaljuve

    oops, don’t have much experience with CommunityServer text formatting.. I’ll try adding pre tags:

    public static Menu BuildMenu()
    {
        var menu =
            new Menu()
                .AddItem("Products", new Menu()
                    .AddItem("Hardware", new Menu()
                        .AddItem("Computers")
                        .AddItem("Printers"))
                    .AddItem("Software"))
                .AddItem("Services", new Menu()
                    .AddItem("Consulting")
                    .AddItem("On-Site Support"))
                .AddItem("Support")
                .AddItem("Contact");
    
        return menu;
    }