Home » Avoid… » Loop-switch sequences

Loop-switch sequences

Overview

The first article which I wrote here was dedicated to the switch statements. However, it was covering only the regular usage of switch statements, a kind which usually can be replaced with polymorphism. There are also another, let’s say ‘unorthodox‘ ways to use switch statements. Some developers combine them with loops in order to execute a sequence of steps, like a sequence of method calls. Surely there are cases where this can totally make sence, but at least if all the steps are known in advance, this is considered to be an anti-pattern called “loop-switch sequence”(read more at wikipedia).

Sometimes it is required to interrupt this sequence if any of the methods failed. The reason behind is the way the error handling is implemented. This article covers loop-switch sequences and reveals a not optimal implementation of error handling as the cause for a special case of them. It shows how the same functionality can be implemented differently, with less lines of code, in a simpler way which is much more straight forward and therefore easier to understand, to debug and to make changes to.

All examples are in C#.

Example of loop-switch sequence

	do
	{
		switch (step)
		{
			case 1:
				result = DoSomething();
				break;
			case 2:
				result = DoSomethingElse();
				break;
			case 3:
				result = DoTheNextThing();
				break;
			case 4:
				result = DoAnotherThing();
				break;
			case 5:
				result = DoTheLastThing();
				break;
		}
	} while (result && step++ <= 5);

Why to avoid

  • Using switch statements just to call methods in a predefined order prevents code from being straight forward. This makes code less readable. It’s simply putting the switch statements to the place where they are not supposed to be, making the intention unclear.
  • In order to add another method call, for example between 1st and 2nd methods, one would need to manually change several hard-coded numbers. In example above it would be changing 5 to 6 – the total number of method calls in loop condition – and increasing all the case-numbers from 2 to 5. The need to manually adjust the numbers in order to simply add a method call is an indicator that this piece of code should be done differently.

Alternative

Just call the methods after each other. Keep it simple!

Coding

An easy case, return value doesn’t matter

Here is an easy case – a sequence of method calls without evaluating their return values. I call it an easy case, because there is obviously no need for loop and switch statement. Placing a switch statement here looks like a love declaration to it, carved into the code. I think that the simplicity of the refactored code on the right side speaks for itself.

Switch statement in a loop:

for (int i = 1; i <= 5; i++)
{
		switch (i)
		{
		case 1:
			DoSomething();
			break;
		case 2:
			DoSomethingElse();
			break;
		case 3:
			DoTheNextThing();
			break;
		case 4:
			DoAnotherThing();
			break;
		case 5:
			DoTheLastThing();
			break;
	}
}
  Refactored, same functionality:

DoSomething();
DoSomethingElse();
DoTheNextThing();
DoAnotherThing();
DoTheLastThing();

Less obvious case, return value does matter

In the following example every method has a boolean return value, which is true if the method did its job right and false otherwise. The loop will break if any of the methods will return false, and the sequence of method calls will be stopped.

bool LogicToDoSeveralThings()
{
	int step = 1;
	bool result = true;
	do
	{
		switch (step)
		{
			case 1:
				result = DoSomething();
				break;
			case 2:
				result = DoSomethingElse();
				break;
			case 3:
				result = DoTheNextThing();
				break;
			case 4:
				result = DoAnotherThing();
				break;
			case 5:
				result = DoTheLastThing();
				break;
		}
	} while (result && step++ <= 5);
	return result;
}

For this example let’s assume, that the parent method is called somewhere in UI like this:

var successfullyDone = LogicToDoSeveralThings()
if (!successfullyDone)
{
	ShowErrorMessage("Something went wrong, sorry");
	return;
}
ShowSuccessIcon();

Here, other than in the easy case above, the methods, as they are implemented right now, can’t be simply called after each other. The main question is – is it important to stop if any of these methods failed? Or is it allowed to call all the next methods of the sequence?

It is required only to know if there was any failure at all

Let’s assume that it is only required to know whether all methods were successful or any of them failed. Let’s assume that a sequence of method calls is allowed to run until the end without interruption, even if the first method already returned false. In this case the code can be written like that:

bool result = DoSomething();
result &= DoSomethingElse();
result &= DoTheNextThing();
result &= DoAnotherThing();
result &= DoTheLastThing();
return result;

It is required to stop immediately after a failure

If it is really necessary to stop at the first method which was not successful then the switch statement can be replaced with something like:

        if (!DoSomething())
            return false;
        if (!DoSomethingElse())
            return false;
        if (!DoTheNextThing())
            return false;
        if (!DoAnotherThing())
            return false;
        if (!DoTheLastThing())
            return false;

This is not really nice, because every two lines there is a repeated ‘if (!…) return false’ pattern.
In C# the sequence of method calls can be defined as a list of functions, which can be called in a loop like that:

            var thingsToDo = new List<Func<bool>>
            {
                DoSomething,
                DoSomethingElse,
                DoTheNextThing,
                DoAnotherThing,
                DoTheLastThing
            };

            foreach (var function in thingsToDo)
            {
                if (!function.Invoke())
                    return false;
            }
            return true;

or even simpler with Linq:

            var thingsToDo = new List<Func<bool>>
            {
                DoSomething,
                DoSomethingElse,
                DoTheNextThing,
                DoAnotherThing,
                DoTheLastThing
            };
            return thingsToDo.All(function => function.Invoke());

This would do exactly the same what switch statement did.

Refactoring the error handling and bringing the code to its most natural way

The examples above show how to avoid switch statements in favor of code with clearer intentions and fewer lines.
However, the most natural way to do several things after each other is still this one:

DoSomething();
DoSomethingElse();
DoTheNextThing();
DoAnotherThing();
DoTheLastThing();

If you mean to call 5 methods after each other – just simply do it!

Let’s look for the reason why the code in the example above (where the sequence of method calls must be interrupted on failure) can’t be simplified to 5 plain lines. Every called method returns a boolean, telling whether it succeeded or not. In case of success – true, in case of an error – false. Quite often such methods are forced to return false in case something goes wrong by catching all exceptions like this:

bool DoSomething();
{
            try
            {
                        //...here comes the logic
                        if (foobarIsNotAsExpected)
                                    return false;
                        //...here comes more logic
                        return true;
            }
            catch (Exception ex)
            {
                        LogError(ex);
                        return false;
            }
}


That’s one way of error handling – returning an error code. Sometimes it’s not a boolean but an enum value – Error.OK, Error.ThisFailure, Error.ThatFailure, etc.

The alternative is to let exceptions be thrown and catched up on a higher level – in the presentation layer, for example! That’s how the DoSomething method would look like:

void DoSomething();
{
            //...here comes the logic
            if (foobarIsNotAsExpected)
                        throw new FoobarIsNotAsExpectedException();
            //...here comes more logic
}

That’s a way cleaner then above, because:

  • previously this method returned false in case of any exception (Null Reference exception, for example) in the logic and also if something was not as expected. Now you’ll get different exceptions saying precisely what went wrong. So that different error messages can be displayed for users. Yes, different messages could also be logged before, but who will check the logs? Users won’t and, in my experience, in most cases developers won’t either (unless confused users send them their logs). A good error message for the user would be more helpful.
  • it’s simply shorter
  • there are no nested statements and blocks

Instead of having 5 methods with 5 try-catch blocks, the error handling can be done in 1 place in UI resulting in a more precise error message:

Before:

var successfullyDone = LogicToDoSeveralThings()
if (!successfullyDone)
{
	ShowErrorMessage("Something went wrong, sorry");
	return;
}
ShowSuccessIcon();

  After:

try
{
	LogicToDoSeveralThings();
}
catch (Exception ex)
{
	LogError(ex.Message);
	ShowErrorMessage("Following went wrong: " + ex.Message);
	return;
}
ShowSuccessIcon();

Having smaller void methods throwing exceptions without try-catches will make the desired sequence of simple method calls possible.

Here is the final side-by-side comparison:

Before:

bool LogicToDoSeveralThings()
{
	int step = 1;
	bool result = true;
	do
	{
		switch (step)
		{
			case 1:
				result = DoSomething();
				break;
			case 2:
				result = DoSomethingElse();
				break;
			case 3:
				result = DoTheNextThing();
				break;
			case 4:
				result = DoAnotherThing();
				break;
			case 5:
				result = DoTheLastThing();
				break;
		}
	} while (result && step++ <= 5);
	return result;
}
  After:

void LogicToDoSeveralThings()
{
	DoSomething();
	DoSomethingElse();
	DoTheNextThing();
	DoAnotherThing();
	DoTheLastThing();
}

Further reading on error handling

The books, which I primarily refer to, say the same about error handling. Please check out why exception are preferable to returning an error code:

  • Chapter “Prefer Exceptions to Returning Error Codes” in Robert Martin’s “Clean Code” book
  • Chapter “Replace Error Code with Exception” in Martin Fowler’s “Refactoring” book

Level Up Code, November 2013


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: