OdeToCode IC Logo

More On The Death of If-Else

Sunday, December 6, 2009

Aaron Feng posted recently on “The death of if-else, if, and else”. In the post Aaron rewrote some JavaScript conditional checks using a dispatch table type approach.

Following along with Aaron’s post using C#, we’d start with a list of Channel objects:

var channels = new List<Channel>
{
    new Channel { Number = 2,
                  Station = "NBC",
                  ShowTitle = "Saturday Night Live",
                  Genre = "comedy",
                  Repeat = true },
    new Channel { Number = 3,
                  Station = "ESPN",
                  ShowTitle = "College Football",
                  Genre = "football",
                  Repeat = false}
    // ...
};

Then we have the logic for deciding which channels to record:

public void Surf(IEnumerable<Channel> channels)
{
    foreach (var channel in channels)
    {
        if (channel.Genre == "football")
        {
            Record(channel);
        }
        else if (channel.Genre == "comedy" && 
                 !channel.Repeat)
        {
            Record(channel);
        }
        else if (channel.Genre == "crime" &&
                channel.ShowTitle != "Cops!")
        {
            Record(channel);
        }
    }
}

Rewriting the code using Aaron’s final approach would look like the following:

public void Surf2(IEnumerable<Channel> channels)
{
    var dispatch = new Dictionary<string, Action<Channel>>
    {
        { "football", c => Record(c) },
        { "comedy",   c => {if(!c.Repeat) Record(c);}},
        { "crime",    c => {if(c.ShowTitle != "Cops!") Record(c);}}
    };

    foreach (var channel in channels)
    {
        dispatch[channel.Genre](channel);
    }
}

Personally, I feel Aaron’s dispatch table is not a big improvement over the previous “if else” version. The actions in the dispatch table are too busy. I think a cleaner approach is to just extract the rules into a data structure – essentially build a collections of predicates to evaluate. Given a channel, the data structure can tell me if the channel should be recorded.

public void Surf3(IEnumerable<Channel> channels)
{
    var recordingRules = new Func<Channel, bool>[]
    {
        c => c.Genre == "football",
        c => c.Genre == "comedy" && !c.Repeat,
        c => c.Genre == "crime" && c.ShowTitle != "Cops!"
    };

    foreach (var channel in channels)
    {
        if(recordingRules.Any(rule => rule(channel) == true))
        {
            Record(channel);
        }
    }
}

Not quite as flexible, but for this specific example its easier to read and maintain.

What do you think? Is that better or worse?