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).

Kanini
  • 2,258

7 Answers7

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.

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...)

Murph
  • 7,843
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.

hugomg
  • 2,102
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.

Telastyn
  • 110,259
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...)

Izkata
  • 6,118
  • 7
  • 30
  • 44
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.

scarfridge
  • 1,816
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.