Switch statements are bad? [closed]

2019-01-09 05:17发布

I recently learned that switch statements are bad in OOP, perticularly from "Clean Code"(p37-39) by Robert Martin.

But consider this scene: I'm writing a game server, receiving messages from clients, which contain an integer that indicates player's action, such as move, attack, pick item... etc, there will be more than 30 different actions. When I'm writing code to handle these messages, no metter what solutions I think about, it will have to use switch somewhere. What pattern should I use if not switch statement?

8条回答
小情绪 Triste *
2楼-- · 2019-01-09 05:44

The Strategy pattern comes to mind.

The strategy pattern is intended to provide a means to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. The strategy pattern lets the algorithms vary independently from clients that use them.

In this case, the "family of algorithms" are your different actions.


As for switch statements - in "Clean Code", Robert Martin says that he tries to limit himself to one switch statement per type. Not eliminate them altogether.

The reason is that switch statements do not adhere to OCP.

查看更多
爷的心禁止访问
3楼-- · 2019-01-09 05:48

A switch is like any other control structure. There are places where it's the best/cleanest solution, and many more places where it's completely inappropriate. It's just abused way more than other control structures.

In OO design, it's generally considered preferable in a situation like yours to use different message types/classes that inherit from a common message class, then use overloaded methods to "automatically" differentiate between the different types.

In a case like yours, you could use an enumeration that maps to your action codes, then attach an attribute to each enumerated value that will let you use generics or type-building to build different Action sub-class objects so that the overloading method will work.

But that's a real pain.

Evaluate whether there's a design option such as the enumeration that is feasible in your solution. If not, just use the switch.

查看更多
Animai°情兽
4楼-- · 2019-01-09 05:48

Use commands. Wrap the action in an object and let polymorphism do the switch for you. In C++ (shared_ptr is simply a pointer, or a reference in Java terms. It allows for dynamic dispatch):

void GameServer::perform_action(shared_ptr<Action> op) {
    op->execute();
}

Clients pick an action to perform, and once they do they send that action itself to the server so the server doesn't need to do any parsing:

void BlueClient::play() {
    shared_ptr<Action> a;
    if( should_move() ) a = new Move(this, NORTHWEST);
    else if( should_attack() ) a = new Attack(this, EAST);
    else a = Wait(this);
    server.perform_action(a);
}
查看更多
女痞
5楼-- · 2019-01-09 05:49

I don't buy it. These OOP zealots seem to have machines that have infinite RAM and amazing performance. Obviously with inifinite RAM you don't have to worry about RAM fragmentation and the performance impacts that has when you continuously create and destroy small helper classes. To paraphrase a quote for the 'Beautiful Code' book - "Every problem in Computer Science can be solved with another level of abstraction"

Use a switch if you need it. Compilers are pretty good at generating code for them.

查看更多
放我归山
6楼-- · 2019-01-09 05:50

'Bad' switch statements are often those switching on object type (or something that could be an object type in another design). In other words hardcoding something that might be better handled by polymorphism. Other kinds of switch statements might well be OK

You will need a switch statement, but only one. When you receive the message, call a Factory object to return an object of the appropriate Message subclass (Move, Attack, etc), then call a message->doit() method to do the work.

That means if you add more message types, only the factory object has to change.

查看更多
兄弟一词,经得起流年.
7楼-- · 2019-01-09 05:52

IMO switch statements are not bad, but should be avoided if possible. One solution would be to use a Map where the keys are the commands, and the values Command objects with an execute() method. Or a List if your commands are numeric and have no gaps.

However, usually, you would use switch statements when implementing design patterns; one example would be to use a Chain of responsibility pattern to handle the commands given any command "id" or "value". (The Strategy pattern was also mentionned.) However, in your case, you might also look into the Command pattern.

Basically, in OOP, you'll try to use other solutions than relying on switch blocks, which use a procedural programming paradigm. However, when and how to use either is somewhat your decision. I personally often use switch blocks when using the Factory pattern etc.


A definition of code organisation is :

  • a package is a group of classes with coherant API (ex: Collection API in many frameworks)
  • a class is a set of coherent functionalities (ex: a Math class...
  • a method is a functionality; it should do one thing and one thing only. (ex: adding an item in a list may require to enlarge that said list, in which case the add method will rely on other methods to do that and will not perform that operation itself, because it's not it's contract.)

Therefore, if your switch statement perform different kinds of operations, you are "violating" that definition; whereas using a design pattern does not as each operation is defined in it's own class (it's own set of functionalities).

查看更多
登录 后发表回答