views:

3664

answers:

23

What are the ways to eliminate the use of switch in code?

+8  A: 

Everybody loves HUGE if else blocks. So easy to read! I am curious as to why you would want to remove switch statements, though. If you need a switch statement, you probably need a switch statement. Seriously though, I'd say it depends on what the code's doing. If all the switch is doing is calling functions (say) you could pass function pointers. Whether it's a better solution is debatable.

Language is an important factor here also, I think.

Bernard
Not everybody. :)
jop
Im assuming that was sarcasm :)
Craig
+13  A: 

I think the best way is to use a good Map. Using a dictionary you can map almost any input to some other value/object/function.

your code would look something(psuedo) like this:

void InitMap(){
    Map[key1] = Object/Action;
    Map[key2] = Object/Action;
}

Object/Action DoStuff(Object key){
    return Map[key];
}
Josh
Depends on the language. It can get a lot less readable than a switch
Vinko Vrsalovic
This is a nice elegent solution in the right situation. I did this mapping keycodes recently, and it seemed to read okay for that purpose.
Bernard
This is true, I probably wouldn't use this for anything simple, but it does provide some amount of flexibility in terms of configuration over a switch statement. A dictionary can be prepared on the fly while a switch will always be hard coded.
Josh
Can be cleaner in some circumstances. Also slower, since it requires function calls.
Nick Johnson
If your dictionary is using a decently implemented hash table under the hood then performance degradation should be minimal.
Josh
That depends on your keys. A compiler can compile a switch statement into either a simple lookup or an extremely fast static binary search, in either case without requiring any function calls.
Nick Johnson
This is true. I would assume the goal here is readability/maintainability. Massive switch statements can quickly become unwieldy and lend themselves to some junior programmer to shove other stuff in there that doesn't belong.You are correct about compilers though. No need to try and be smarter.
Josh
In fact, I have seen compilers which are able to recognize such a mapping pattern and compile it into a switch-statement. :)
Tetha
+3  A: 

if-else

I refute the premise that switch is inherently bad though.

IainMH
+3  A: 

Well, for one, I didn't know using switch was an anti pattern.

Secondly, switch can always be replaced with if / else if statements.

SCdF
exactly - switch is just syntactic methadone for a bunch of if/elsifs.
A: 

The most obvious, language independent, answer is to use a series of 'if'.

If the language you are using has function pointers (C) or has functions that are 1st class values (Lua) you may achieve results similar to a "switch" using an array (or a list) of (pointers to) functions.

You should be more specific on the language if you want better answers.

Remo.D
+2  A: 

Why do you want to? In the hands of a good compiler, a switch statement can be far more efficient than if/else blocks (as well as being easier to read), and only the largest switches are likely to be sped up if they're replaced by any sort of indirect-lookup data structure.

Nick Johnson
At which point you're second-guessing the compiler, and making changes to your design based on compiler internals. Design should follow the nature of the problem, not the nature of the compiler.
+23  A: 

Switch in itself isn't that bad, but if you have lots of "switch" or "if/else" on objects in your methods it may be a sign that your design is a bit "procedural" and that your objects are just value buckets. Move the logic to your objects, invoke a method on your objects and let them decide how to respond instead.

Lars Westergren
Assuming of course, that he's not writing in C. :)
Bernard
in C, he can (ab?)use function pointers and structs to build something object-like ;)
Tetha
You can write FORT^H^H^H^H Java in any language. ;p
Bernard
Oh God, I'm a dead man now aren't I? I didn't mean it guys! :D
Bernard
+1  A: 

What about cyclomatic complexity?

Mariano
what about common sense? reducing cyclomatic complexity doesn't mean "kill all switches". May be it would be better to reformulate your question? For example "When to not use switch statement?"
aku
A program written using switch and program that performs the same action and written using say, virtual functions, must surely have the same cyclomatic complexity?
Skizz
Skizz, nope. cyclomatic complexity can be reduced by reducing number of if/switches. For example by using map(dictionary) of functions instead of N switch cases. But question is worded quite strange IMO
aku
Is it possible to turn this post into a community editable post so everybody's answers can be consolidated into an informative post about the pros/cons of using switch statements and how to refactor one into something like a Map/Dictionary.
Josh
I am an spanish speaker, maybe that´s why "the question is worded quite strange". Thank you all
Mariano
Mariano, your English looks far better than mine. "How to get rid of X" sounds quite strange. First reaction would be "Why the Hell you want to do it?" I don't say that question is bad. It just sounds strange.
aku
A dictionary of functions is logically equivalent to a switch with the same behavior. Any tool which assigns different cyclomatic complexities is broken in this respect. A set of if/else if statements is even more complex; it's unobvious whether you consistently check the same variable.
MSalters
+2  A: 

'switch' is just a language construct and all language constructs can be thought of as tools to get a job done. As with real tools, some tools are better suited to one task than another (you wouldn't use a sledge hammer to put up a picture hook). The important part is how 'getting the job done' is defined. Does it need to be maintainable, does it need to be fast, does it need to scale, does it need to be extendable and so on.

At each point in the programming process there are usually a range of constructs and patterns that can be used: a switch, an if-else-if sequence, virtual functions, jump tables, maps with function pointers and so on. With experience a programmer will instinctively know the right tool to use for a given situation.

It must be assumed that anyone maintaining or reviewing code is at least as skilled as the original author so that any construct can be safely used.

Skizz

Skizz
OK, but why do we need 5 different, redundant ways of doing the same thing - conditional execution?
@mike.amy: Because each method has different benefits and costs and it's all about getting the most benefit with the least cost.
Skizz
+1  A: 

If the switch is there to distinguish between various kinds of objects, you're probably missing some classes to precisely describe those objects, or some virtual methods...

Xavier Nodet
+17  A: 

A switch is a pattern, whether implemented with a switch statement, if else chain, lookup table, oop polymorphism, pattern matching or something else.

Do you want to eliminate the use of the "switch statement" or the "switch pattern"? The first one can be eliminated, the second one, only if another pattern/algorithm can be used, and most of the time that is not possible or it's not a better approach to do so.

If you want to eliminate the switch statement from code, the first question to ask is where does it make sense to eliminate the switch statement and use some other technique. Unfortunately the answer to this question is domain specific.

And remember that compilers can do various optimizations to switch statements. So for example if you want to do message processing efficiently, a switch statement is pretty much the way to go. But on the other hand running business rules based on a switch statement is probably not the best way to go and the application should be rearchitected.

Here are some alternatives to switch statement :

Pop Catalin
Could someone give a comparison of message processing using switch versus other alternatives?
A: 

Switch statements can often be replaced by a good OO design.

For example, you have an Account class, and are using a switch statement to perform a different calculation based on the type of account.

I would suggest that this should be replaced by a number of account classes, representing the different types of account, and all implementing an Account interface.

The switch then becomes unnecessary, as you can treat all types of accounts the same and thanks to polymorphism, the appropriate calculation will be run for the account type.

jason
+67  A: 

Switch-statements are not an antipattern per se, but if you're coding object oriented you should consider if the use of a switch is better solved with polymorphism instead

With polymorphism

foreach (var animal in zoo) {
    switch (typeof(animal)) {
        case "dog":
            echo animal.bark();
            break;

        case "cat":
            echo animal.meow();
            break;
    }
}

becomes

foreach (var animal in zoo) {
    echo animal.speak();
}
mlarsen
Polkymorphism? Don't they do that in Barvaria?
Nick Johnson
I upmodded it, as it's 100% correct - but please edit the typos!
slim
Great example, +1
Jonas Gulle
I being bashed because of similar suggestion in http://stackoverflow.com/questions/374239/why-doesnt-python-have-a-switch-statement#374290 So many ppl don't believe in polymorphism :) Very good example.
Nazgob
+2  A: 

I think that what you are looking for is the Strategy Pattern.

This can be implemented in a number of ways, which have been mentionned in other answers to this question, such as:

  • A map of values -> functions
  • Polymorphism. (the sub-type of an object will decide how it handles a specific process).
  • First class functions.
AJ
A: 

Another vote for if/else. I'm not a huge fan of case or switch statements because there are some people that don't use them. The code is less readable if you use case or switch. Maybe not less readable to you, but to those that have never needed to use the command.

The same goes for object factories.

If/else blocks are a simple construct that everyone gets. There's a few things you can do to make sure that you don't cause problems.

Firstly - Don't try and indent if statements more than a couple of times. If you're finding yourself indenting, then you're doing it wrong.

 if a = 1 then 
     do something else 
     if a = 2 then 
         do something else
     else 
         if a = 3 then 
             do the last thing
         endif
     endif 
  endif

Is really bad - do this instead.

if a = 1 then 
   do something
endif 
if a = 2 then 
   do something else
endif 
if a = 3 then 
   do something more
endif

Optimisation be damned. It doesn't make that much of a difference to the speed of your code.

Secondly, I'm not averse to breaking out of an If Block as long as there are enough breaks statements scattered through the particular code block to make it obvious

procedure processA(a:int)
    if a = 1 then 
       do something
       procedure_return
    endif 
    if a = 2 then 
       do something else
       procedure_return
    endif 
    if a = 3 then 
       do something more
       procedure_return
    endif 
end_procedure

EDIT: On Switch and why I think it's hard to grok:

Here's an example of a switch statement...

private void doLog(LogLevel logLevel, String msg) {
   String prefix;
   switch (logLevel) {
     case INFO:
       prefix = "INFO";
       break;
     case WARN:
       prefix = "WARN";
       break;
     case ERROR:
       prefix = "ERROR";
       break;
     default:
       throw new RuntimeException("Oops, forgot to add stuff on new enum constant");
   }
   System.out.println(String.format("%s: %s", prefix, msg));
 }

For me the issue here is that the normal control structures which apply in C like languages have been completely broken. There's a general rule that if you want to place more than one line of code inside a control structure, you use braces or a begin/end statement.

e.g.

for i from 1 to 1000 {statement1; statement2}
if something=false then {statement1; statement2}
while isOKtoLoop {statement1; statement2}

For me (and you can correct me if I'm wrong), the Case statement throws this rule out of the window. A conditionally executed block of code is not placed inside a begin/end structure. Because of this, I believe that Case is conceptually different enough to not be used.

Hope that answers your questions.

seanyboy
Wow - obviously a contentious answer. I'd be interested in knowing what I got so wrong.
seanyboy
Er, switch as being too complex? I don't know... seems to me there wouldn't be many language features left you could use. :)Also, in your central example, wouldn't it be smarter to do if (a==1 || a==2 || a==3) do something?
Lars Westergren
Also, in your last example, "break" won't do anything in most languages-that breaks out of the nearest block (usually a loop), which in your case happens anyway on the next line (endif). If you use a language where break is "return", many returns are also frowned upon (except for "guard statements")
Lars Westergren
"Optimisation be damned. It doesn't make that much of a difference to the speed of your code." My code runs on a mobile platform. Optimizations matter.Further, switches can make code look VERY clean (compared to if..elseif..elseif..elseif...) if used correctly. Never seen them? Learn them.
Swati
Switch isn't complicated at all, but I've always tried to minimise the number of constructs used in code to reduce friction in understanding.
seanyboy
(2) You're right about the incorrect use of Break and "do something". I'll update the examples. (3) Your code may run on a mobile platform, and in this situation, then optimisation is a good thing. But generally, you shouldn't optimise until you have to. Readability comes first.
seanyboy
I've updated my example to show why I consider Switch to be difficult.
seanyboy
Welcome to the club of contentious answers. I'm a charter member. C was made by/for hairy OS coders. I suspect they wanted something that would easily generate a jump table, which sometimes is fast. They didn't care too much about "niceness" then.
Mike Dunlavey
+45  A: 

See the Switch Statements Smell:

Typically, similar switch statements are scattered throughout a program. If you add or remove a clause in one switch, you often have to find and repair the others too.

Both Refactoring and Refactoring to Patterns have approaches to resolve this.

If your (pseudo) code looks like:

class RequestHandler {

    public void handleRequest(int action) {
        switch(action) {
            case LOGIN:
                doLogin();
                break;
            case LOGOUT:
                doLogout();
                break;
            case QUERY:
               doQuery();
               break;
        }
    }
}

This code violates the Open Closed Principle and is fragile to every new type of action code that comes along. To remedy this you could introduce a 'Command' object:

interface Command {
    public void execute();
}

class LoginCommand implements Command {
    public void execute() {
        // do what doLogin() used to do
    }
}

class RequestHandler {
    private Map<Integer, Command> commandMap; // injected in, or obtained from a factory
    public void handleRequest(int action) {
        Command command = commandMap.get(action);
        command.execute();
    }
}

If your (pseudo) code looks like:

class House {
    private int state;

    public void enter() {
        switch (state) {
            case INSIDE:
                throw new Exception("Cannot enter. Already inside");
            case OUTSIDE:
                 state = INSIDE;
                 ...
                 break;
         }
    }
    public void exit() {
        switch (state) {
            case INSIDE:
                state = OUTSIDE;
                ...
                break;
            case OUTSIDE:
                throw new Exception("Cannot leave. Already outside");
        }
    }

Then you could introduce a 'State' object.

abstract class HouseState {
    public HouseState enter() {
        throw new Exception("Cannot enter");
    }
    public HouseState leave() {
        throw new Exception("Cannot leave");
    }
}
class Inside extends HouseState {
    public HouseState leave() {
        return new Outside();
    }
}
class Outside extends HouseState {
    public HouseState enter() {
        return new Inside();
    }
}

class House {
    private HouseState state;
    public void enter() {
        this.state = this.state.enter();
    }
    public void leave() {
        this.state = this.state.leave();
    }
}

Hope this helps.

toolkit
Thanks for the great example on how to refactor code. Though I might say in the beggining it is a little hard to read (because one has to switch between several files to completely understand it)
rshimoda
glad to help :-)
toolkit
Upvoted, but please fix typos. ..impelements.. ..psuedo..
Schalk Versteeg
The arguments against switch are valid as long as you realize that the polymorphic solution sacrifices code simplicity. In addition, if you always store your switch cases in enums, some compilers will warn you that states are missing from the switch.
Harvey
This is a great example about complete/incomplete operations and of course re-structuring code into OOP. Thanks a ton. I think it will be useful if OOP / Design Patterns proponents **suggest treating OOP concepts like operators rather than concepts**. I mean that "extends", "factory", "implements", etc are used so often across files, classes, branches. They should be simple as operators like "+", "-", "+=", "?:", "==", "->" etc. When a programmer uses them in his mind as simply as operators, only then he can think across the full class library about program state and (in)complete operations.
namespaceform
A: 

Depends why you want to replace it!

Many interpreters use 'computed gotos' instead of switch statements for opcode execution.

What I miss about C/C++ switch is the Pascal 'in' and ranges. I also wish I could switch on strings. But these, while trivial for a compiler to eat, are hard work when done using structures and iterators and things. So, on the contrary, there are plenty of things I wish I could replace with a switch, if only C's switch() was more flexible!

Will
+1  A: 

For C++

If you are referring to ie an AbstractFactory I think that a registerCreatorFunc(..) method usually is better than requiring to add a case for each and every "new" statement that is needed. Then letting all classes create and register a creatorFunction(..) which can be easy implemented with a macro (if I dare to mention). I believe this is a common approach many framework do. I first saw it in ET++ and I think many frameworks that require a DECL and IMPL macro uses it.

epatel
+3  A: 

switch statements would be good to replace if you find yourself adding new states or new behaviour to the statements:

int state;

String getString() {
   switch (state) {
     case 0 : // behaviour for state 0
           return "zero";
     case 1 : // behaviour for state 1
           return "one";
   }
   throw new IllegalStateException();
}

double getDouble() {

   switch (this.state) {
     case 0 : // behaviour for state 0
           return 0d;
     case 1 : // behaviour for state 1
           return 1d;
   }
   throw new IllegalStateException();
}

Adding new behaviour requires copying the switch, and adding new states means adding another case to every switch statement.

In Java, you can only switch a very limited number of primitive types whose values you know at runtime. This presents a problem in and of itself: states are being represented as magic numbers or characters.

Pattern matching, and multiple if - else blocks can be used, though really have the same problems when adding new behaviours and new states.

The solution which others have suggested as "polymorphism" is an instance of the State pattern:

Replace each of the states with its own class. Each behaviour has its own method on the class:

IState state;

String getString() {
   return state.getString();
}

double getDouble() {
   return state.getDouble();
}

Each time you add a new state, you have to add a new implementation of the IState interface. In a switch world, you'd be adding a case to each switch.

Each time you add a new behaviour, you need to add a new method to the IState interface, and each of the implementations. This is the same burden as before, though now the compiler will check that you have implementations of the new behaviour on each pre-existing state.

Others have said already, that this may be too heavyweight, so of course there is a point you reach where you move from one to another. Personally, the second time I write a switch is the point at which I refactor.

jamesh
A: 

In a procedural language, like C, then switch will be better than any of the alternatives.

In an object-oriented language, then there are almost always other alternatives available that better utilise the object structure, particularly polymorphism.

The problem with switch statements arises when several very similar switch blocks occur at multiple places in the application, and support for a new value needs to be added. It is pretty common for a developer to forget to add support for the new value to one of the switch blocks scattered around the application.

With polymorphism, then a new class replaces the new value, and the new behaviour is added as part of adding the new class. Behaviour at these switch points is then either inherited from the superclass, overridden to provide new behaviour, or implemented to avoid a compiler error when the super method is abstract.

Where there is no obvious polymorphism going on, it can be well worth implementing the Strategy pattern.

But if your alternative is a big IF ... THEN ... ELSE block, then forget it.

Bill Michell
+1  A: 

Use a language that doesn't come with a built-in switch statement. Perl 5 comes to mind.

Seriously though, why would you want to avoid it? And if you have good reason to avoid it, why not simply avoid it then?

innaM
A: 

Function pointers are one way to replace a huge chunky switch statement, they are especially good in languages where you can capture functions by their names and make stuff with them.

Of course, you ought not force switch statements out of your code, and there always is a chance you are doing it all wrong, which results with stupid redundant pieces of code. (This is unavoidable sometimes, but a good language should allow you to remove redundancy while staying clean.)

This is a great divide&conquer example:

Say you have an interpreter of some sort.

switch(*IP) {
    case OPCODE_ADD:
        ...
        break;
    case OPCODE_NOT_ZERO:
        ...
        break;
    case OPCODE_JUMP:
        ...
        break;
    default:
        fixme(*IP);
}

Instead, you can use this:

opcode_table[*IP](*IP, vm);

... // in somewhere else:
void opcode_add(byte_opcode op, Vm* vm) { ... };
void opcode_not_zero(byte_opcode op, Vm* vm) { ... };
void opcode_jump(byte_opcode op, Vm* vm) { ... };
void opcode_default(byte_opcode op, Vm* vm) { /* fixme */ };

OpcodeFuncPtr opcode_table[256] = {
    ...
    opcode_add,
    opcode_not_zero,
    opcode_jump,
    opcode_default,
    opcode_default,
    ... // etc.
};

Note that I don't know how to remove the redundancy of the opcode_table in C. Perhaps I should make a question about it. :)

Cheery
A: 

Switch is not a good way to go as it breaks the Open Close Principal. This is how I do it.

public class Animal { public abstract void Speak(); }

public class Dog : Animal { public virtual void Speak() { Console.WriteLine("Hao Hao"); } }

public class Cat : Animal { public virtual void Speak() { Console.WriteLine("Meauuuu"); } }

And here is how to use it (taking your code) foreach (var animal in zoo) { echo animal.speak(); }

Basically what we are doing is delegating the responsibility to the child class instead having the parent to decide what to do with children. 
   You might also want to read up on "Liskov Substitution Principle"

Hope it helped.

Sheraz