Ways to eliminate switch in code [closed]

2018-12-31 18:08发布

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

23条回答
美炸的是我
2楼-- · 2018-12-31 18:45

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.

查看更多
一个人的天荒地老
3楼-- · 2018-12-31 18:45

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.

查看更多
不流泪的眼
4楼-- · 2018-12-31 18:48

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

查看更多
低头抚发
5楼-- · 2018-12-31 18:48

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 of having the parent decide what to do with children.

You might also want to read up on "Liskov Substitution Principle".

查看更多
无与为乐者.
6楼-- · 2018-12-31 18:49

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

查看更多
回忆,回不去的记忆
7楼-- · 2018-12-31 18:50

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.

查看更多
登录 后发表回答