What are ways of eliminating the use of switch
statements in code?
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 of using a switch statement.
With polymorphism, this:
foreach (var animal in zoo) {
switch (typeof(animal)) {
case "dog":
echo animal.bark();
break;
case "cat":
echo animal.meow();
break;
}
}
becomes this:
foreach (var animal in zoo) {
echo animal.speak();
}
typeof
, and this answer doesn't suggest ways or reasons to work around switch statements in other situations. –
Ovotestis 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();
}
}
Map<Integer, Command>
not need a switch? –
Darfur map.add(i => someCommand)
for each command you wish to add. This gives you (and other classes) flexibility in adding additional commands. But for fairly static stuff, I think swtich
statements are cleaner and more obvious... –
Curmudgeon HouseState
why did you create abstract class instead of interface? –
Symbolic 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 :
- lookup table
- polymorphism
- pattern matching (especially used in functional programming, C++ templates)
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.
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];
}
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.
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.
'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.
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.
if-else
I refute the premise that switch is inherently bad though.
Well, for one, I didn't know using switch was an anti pattern.
Secondly, switch can always be replaced with if / else if statements.
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.
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.
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".
In JavaScript using Associative arrays, this:
function getItemPricing(customer, item) {
switch (customer.type) {
// VIPs are awesome. Give them 50% off.
case 'VIP':
return item.price * item.quantity * 0.50;
// Preferred customers are no VIPs, but they still get 25% off.
case 'Preferred':
return item.price * item.quantity * 0.75;
// No discount for other customers.
case 'Regular':
case
default:
return item.price * item.quantity;
}
}
becomes this:
function getItemPricing(customer, item) {
var pricing = {
'VIP': function(item) {
return item.price * item.quantity * 0.50;
},
'Preferred': function(item) {
if (item.price <= 100.0)
return item.price * item.quantity * 0.75;
// Else
return item.price * item.quantity;
},
'Regular': function(item) {
return item.price * item.quantity;
}
};
if (pricing[customer.type])
return pricing[customer.type](item);
else
return pricing.Regular(item);
}
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...
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.
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. :)
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.
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.
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!
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?
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.
© 2022 - 2024 — McMap. All rights reserved.