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?
The
Strategy
pattern comes to mind.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.
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.
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):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:
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.
'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.
IMO
switch
statements are not bad, but should be avoided if possible. One solution would be to use aMap
where the keys are the commands, and the valuesCommand
objects with anexecute()
method. Or aList
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 useswitch
blocks when using the Factory pattern etc.A definition of code organisation is :
Collection
API in many frameworks)Math
class...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).