29

I was reading this article and was wondering, do we get rid of all switch statements by replacing them with a Dictionary or a Factory so that there are no switch statements at all in my projects.

Something did not quite add up.

The question is, do switch statements have any real use or do we go ahead and replace them with either a dictionary or a factory method (in using a factory method, of course, there will be a minimum use of switch statements for creating the objects using the factory...but that is about it).

5

7 Answers 7

48

Both switch statements and polymorphism have their use. Note though that a third option exists too (in languages which support function pointers / lambdas, and higher-order functions): mapping the identifiers in question to handler functions. This is available in e.g. C which is not an OO language, and C# which is*, but not (yet) in Java which is OO too*.

In some procedural languages (having no polymorphism nor higher-order functions) switch / if-else statements were the only way to solve a class of problems. So many developers, having accustomed to this way of thinking, continued to use switch even in OO languages, where polymorphism is often a better solution. This is why it is often recommended to avoid / refactor switch statements in favour of polymorphism.

At any rate, the best solution is always case dependent. The question is: which option gives you cleaner, more concise, more maintainable code in the long run?

Switch statements can often grow unwieldy, having dozens of cases, making their maintenance hard. Since you have to keep them in a single function, that function can grow huge. If this is the case, you should consider refactoring towards a map based and/or polymorphic solution.

If the same switch starts to pop up in multiple places, polymorphism is probably the best option to unify all these cases and simplify code. Especially if more cases are expected to be added in the future; the more places you need to update each time, the more possibilities for errors. However, often the individual case handlers are so simple, or there are so many of them, or they are so interrelated, that refactoring them into a full polymorphic class hierarchy is overkill, or results in a lot of duplicated code and/or tangled, hard to maintain class hierarchy. In this case, it may be simpler to use functions / lambdas instead (if your language allows you).

However, if you have a switch in a single place, with only a few cases doing something simple, it may well be the best solution to leave it like it is.

*I use the term "OO" loosely here; I am not interested in conceptual debates over what is "real" or "pure" OO.

2
  • 4
    +1. Also, those recommendations tend to be worded, "prefer polymorphism to switch" and that's a good word choice, very much in keeping with this answer. It acknowledges that there are circumstances where a responsible coder might make the other choice. Commented May 4, 2012 at 13:30
  • 1
    And there are times when you want the switch approach even if there are a gazillion of them in your code. I've got a piece of code in mind that generates a 3D maze. Memory use would go way up if the one-byte cells in the array were replaced with classes. Commented Feb 14, 2013 at 4:32
15

This is where I go back to being a dinosaur...

Switch statements are not bad in an of themselves, its the use that's made of them that's at issue.

The most obvious one is the "same" switch statement repeated over and over again through your code that is bad (been there, done that, would make every effort not to again) - and its this latter case that you may be able to deal with using polymorphism. There's usually something fairly horrid about nested cases too (I used to have an absolute monster - not entirely sure how I deal with it now other than "better").

Dictionary as switch I find more challenging - fundamentally yes if your switch covers 100% of the cases, but where you want to have default or no action cases it starts to get a bit more interesting.

I think its a question of avoiding repetition and of making sure we're composing our object graphs in the right places.

But there's also the comprehension (maintainability) argument and this cuts both ways - once you understand how it all works (the pattern and the app in which its implemented) its easy... but if you come to a single line of code where you have to add something new you then have to jump all over the place to work out what you need to add/change.

For all that our development environments are massively capable I still feel that it is desirable to be able to understand code (as if it were) printed out on paper - can you follow the code with your finger? I accept that actually no, with a lot of good practice today you can't and for good reasons but that means that getting up to speed with code is harder to start (or maybe I'm just old...)

2
  • 5
    I had a teacher who said that ideally code should be written "so that someone who knew nothing about programming (like maybe your mother) could understand it." Unfortunately that is an ideal, but maybe we should include our moms in code reviews anyway!
    – Michael K
    Commented May 4, 2012 at 12:35
  • +1 for "but if you come to a single line of code where you have to add something new you then have to jump all over the place to work out what you need to add/change" Commented May 5, 2012 at 5:26
9

Switch-statements vs subtype-polymorphism is an old problem and is often referred to in the FP community in discussions on the Expression Problem.

Basically, we have types (classes) and functions (methods). How do we code stuff so that its easy to add new types or new methods latter on?

If you program in OO style, its hard to add a new method (since that would mean refactoring all the existing classes), but its very easy to add new classes that use the same methods as before.

On the other hand, if you use a switch statement (or its OO equivalent, the Observer Pattern) then its very easy to add new functions but its hard to add new cases/classes.

Its not easy to have good extensibility in both directions, so when writing your code, determine whether to use polymorphism or switch statements depending on which direction you are more likely to extend latter on.

5
do we get rid of all switch statements by replacing them with a Dictionary or a Factory so that there are no switch statements at all in my projects.

No. Such absolutes are rarely a good idea.

Many places, a dictionary/lookup/factory/polymorphic dispatch will provide a better design than a switch statement but you still have to populate the dictionary. In certain cases, that will obscure what is actually going on and simply having the switch statement in-line is more readable and maintainable.

3
  • And really, if you unrolled the factory and the dictionary and the lookup, it would turn out to be basically a switch statement: if array[hash(search)] then call array[hash(search)]. Although it would be runtime-extendable which compiled switches are not.
    – Zan Lynx
    Commented May 4, 2012 at 20:07
  • @ZanLynx, the complete opposite is true, at least with C#. If you look at the IL produced for a non-trivial switch statement, you'll see the compiler turns it into a dictionary.as it is faster.
    – David Arno
    Commented Oct 10, 2015 at 12:56
  • @DavidArno: "the complete opposite"? The way I read it we said exactly the same thing. How can it be opposite?
    – Zan Lynx
    Commented Oct 10, 2015 at 15:43
2

Seeing as how this is language-agnostic, fall-through code works best with switch statements:

switch(something) {
   case 1:
      foo();
   case 2:
      bar();
      baz();
      break;

   case 3:
      bang();
   default:
      bizzap();
      break;
}

Equivalent with if, with a very awkward default case. And keep in mind that the more fall-through there is, the longer the condition list will get:

if (1 == something) {
   foo();
}
if (1 == something || 2 == something) {
   bar();
   baz();
}
if (3 == something) {
   bang();
}
if (1 != something && 2 != something) {
   bizzap();
}

(However, given what some of the other answers say, I feel like I've missed the point of the question...)

1
  • I would regard the use of fall-through statements in a switch as being on the same level as a goto. If the algorithm to be performed requires control flows that fit structured programming constructs, one should use such constructs, but if the algorithm doesn't fit such constructs, using goto may be better than trying to add flags or other control logic to fit other control structures.
    – supercat
    Commented Jul 14, 2015 at 18:13
1

Switch statements as case differentiation do have a real use. Functional programming languages use something called pattern matching which allows you to define functions differently depending on the input. However in object oriented languages the same goal can be reached more elegantly by using polymorphism. You simply call the method and depending on the actual type of the object, the corresponding implementation of the method will be executed. This is the reason behind the idea, that switch statements are a code smell. However, even in OO languages, you might find them useful to implement the abstract factory pattern.

tl;dr - they are useful, but quite often you can do better.

6
  • 2
    I smell some bias there - why is polymorphism more elegant than pattern matching? And what makes you think polymorphism doesn't exist in FP?
    – tdammers
    Commented May 4, 2012 at 8:24
  • @tdammers First of all I did not mean to imply that polymorphism does not exist in FP. Haskell supports ad hoc and parametric polymorphism. Pattern matching makes sense for an algebraic datatype. But for external modules this requires exposing constructors. Clearly this breaks encapsulation of an abstract data type (realized with algebraic datatypes). That is why I feel that polymorphism is more elegant (and possible in FP).
    – scarfridge
    Commented May 4, 2012 at 15:20
  • You are forgetting polymorphism through typeclasses and instances.
    – tdammers
    Commented May 4, 2012 at 16:12
  • Heve you ever heard of the Expression Problem? The OO and the FP are better in different situations, if you stop to think about it.
    – hugomg
    Commented May 4, 2012 at 20:13
  • @tdammers What kind of polymorphism do you think I was talking about when I mentioned ad hoc polymorphism? Ad hoc polymorphism is realized in Haskell through type classes. How can you say I forgot it? Simply not true.
    – scarfridge
    Commented May 5, 2012 at 19:25
0

Polymorphism provides something critical, which switch-statements do not: Bundling different state with different operations.

I will explain with an example: Suppose you are designing a class, which represents a database connection. The connection may be to Oracle Database or MS SQL Server. In this example, an obvious design would be to create an interface, IDBConnection, as well as two classes, OracleConnection and SqlServerConnection. The operations on these classes (e.g. runQuery()) would be implemented using polymorphism. An OracleConnection object would contain a connection object from an Oracle library and a SqlServerConnection object would contain a connection object from some Microsoft library.

Now, imagine what would happen if we implemented everything in the same class, DBConnection, which would contain an enum informing us whether the connection is for Oracle or Microsoft. This class would need two variables to store the underlying connection, one for Microsoft and one for Oracle. At any given time, one of these variables would be null, since only the other one was being used.

In the above example, it is easy to figure out which underlying connection is valid, based on the type code stored. This is not always the case though. I have seen examples in practice with many, many fields where most of them were unused at any given time and it was hard to tell which ones were supposed to be set.

So, whenever you switch on an enum value, you should ask yourself if the enum is/should be bundled with different state for each value. Then you should probably use a class hierarchy instead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.