Is `switch` an anti-pattern?

People say that it's a code smell if you see:

if .... {

} else if ... {

} else if ... {

}
...

However, does that mean switch is bad? Because it looks very much alike:

    switch ... {
         case ....:
         case ....:
}
1 Like

I believe when people say "it's code smell" in the context of too many if branches that all operate on some patterns then they rather mean that switch is the better solution for that. Just my interpretation of your message, nothing more.

4 Likes

It may look similar, but the crucial difference is that when switching on an enum, you'll get an error if any of its cases aren't covered. This is not the case for if ... else if chains.

7 Likes

Those two options are slightly different, and neither is a code smell per se.
However, switch not only ensures that all cases are handled, it's also more compact and has strict limits on what conditions are evaluated:

Imagine you see code like this:

if myNumber == 42 {
	...
} else if myNumber == 23 {
	...
} else if myNumber == 24 {
	...
} else if myNumber == 25 {
	...
} else if myNumber == 26 {
	...
} else if myNumber == 27 {
	...
} else if myNumber == 28 {
	...
} else if myNumber == 29 {
	...
} else if myNumber == 30 {
	...
} else if myNumber == 31 {
	...
} else if myNumber == 32 {
	...
} else if myNumber == 33 {
	...
} else if myNumber == 34 {
	...
} else if myNumber == 35 {
	...
} else if myNumber == 36 {
	...
} else if myNumber == 37 {
	...
} else if myNumber == 38 {
	...
} else if myNumber == 39 {
	...
} else if myNumber == 40 {
	...
} else if Date().secondsSinceMidnight % 5 == 3 {
	...
} else if myNumber == 41 {
	...
} else if myNumber == 42 {
	...
} else if myNumber == 43 {
	...
}

I hope most would agree that this snippet has some serious issues…

None the less, there are surely cases where there is no alternative to else if.

Theoretically the switch statement (at least for simple kind of switch statements, e.g. when a String is compared to several values or in the simple case of enum switches) could be optimized, i.e. it then could be more efficient than following all if statements. If I remember correctly, I have read here somewhere that there is some optimization happening in release mode, but not in debug mode, does anyone know about it? (I do not think that this is documented.)

1 Like

The biggest code small is using either strings or numbers for a limited comparison set - otherwise known as magic numbers or string literals

Take a look at using enums…

  enum MyNumber
  {
    case twentyThree = 23
    case twentyFour
    case twentyFive
    …
  }

Then you a switch statement will not compile unless all possible cases are handled or a default clause is declared.

  let myNumber: MyNumber = .twentyThree

  switch myNumber
  {
    case .twentyThree:
      …
    case .twentyFour:
      …
    case .twentyFive:
      …
  }
2 Likes

Yes, this optimisation we already have, for example if you switch over an enumeration or a range of integers – compiler jumps straight to the right spot without doing a bunch of EQ comparisons. While not massive speed difference it is still good to have.

As Max and others said above the biggest win of switch vs an "equivalent" if-else sequence is its maintainability due to its exhaustiveness † – you add a new enumeration case and forgot to handle it – compiler prompts your attention right away with switch ††, not so with if-else block.

*† - switch exhaustiveness only works with enums.
†† unless there's a default case handling... that's also an anti-pattern (unless unavoidable)

Personally I have never had a problem where I became suddenly compelled to solve all of the problems all in the same place. Usually any notion or thought like that sets off alarms in my mind that are like a loud warning to think harder about the problem. This helps me identify, and then correct that. Solving many things in one place is how we get to dangling else problem etc.

In fact the only time I have seen this work is for error propagation. There are probably other valid scenarios. Just my 2cents

I've always seen this as an architectural code smell: splitting behaviour in one gargantuan place instead of using better tools (i.e. polymorphism).

Otherwise, when you're mapping values, I see little reasons to worry, even if you have to map over a large number of them. switch is better in some cases because of the reasons others have mentioned, but if the set is not bounded, using switch brings minimal gains, if any.