📜 ⬆️ ⬇️

Aesthetic Beauty: Switch vs If

Introductory


As developers, we encounter code every day, and the more we like, we see, we write, the more enthusiastic we get, the more productive and effective we become. What can I say, we are just proud of our code. But one dilemma does not give me peace: when 2 developers look at the same code, they may have completely opposite feelings. And what to do if these feelings, emotions, inspired by its aesthetic beauty, do not coincide with the emotions of most of the professionals around you? In general, the story of why you may not like the language construction of a switch so much that you prefer if. Who is interested in this holivar position welcome under cat.

Few nuances


The following discussion focuses on the comparison of the switch statement and its specific implementations in the Java language and if statements in the form of opponents of this construct. The fact that the switch operator has an ace in the sleeve - performance (with rare exceptions), perhaps, everyone knows and this point will not be considered - because there is nothing to cover it with. But still, what's wrong?

1. Verbosity and bulkiness


Among the reasons that repel me from using the switch operator is the verbosity and cumbersomeness of the construction itself, just take a look - switch, case, break and default, and I omit from the brackets that between them there will probably still be return and throw. Do not think that I urge you not to use the service words of the java language or to avoid a large number of them in the code, no, I just think that simple things should not be wordy. Here is a small example of what I'm talking about:

public List<String> getTypes1(Channel channel) { switch (channel) { case INTERNET: return Arrays.asList("SMS", "MAC"); case HOME_PHONE: case DEVICE_PHONE: return Arrays.asList("CHECK_LIST"); default: throw new IllegalArgumentException("   " + channel); } } 

option with if
')
  public List<String> getTypes2(Channel channel) { if (INTERNET == channel) { return Arrays.asList("SMS", "MAC"); } if (HOME_PHONE == channel || DEVICE_PHONE == channel) { return Arrays.asList("CHECK_LIST"); } throw new IllegalArgumentException("   " + channel); } 

Total for me: switch / case / default VS if.

If you ask who likes which code more, then the majority would prefer the first option, while for me, it is wordy. Speech about refactoring of the code does not go here, we all know that we could use a constant like EnumMap or IdentityHashMap to search the list by the channel key or remove the necessary data from the Channel itself, although this is a debatable decision. But back.

2. Indents


Perhaps in academic examples, the use of the swicth operator is the only part of the code, but the code that you have to deal with is more complex, overgrown with checks, hacks, somewhere just subject complexity. And me personally, every indent in such a place is annoying. Referring to the 'live' example (removed too much, but the essence remained).

  public static List<String> getReference1(Request request, DataSource ds) { switch (request.getType()) { case "enum_ref": return EnumRefLoader.getReference(ds); case "client_ref": case "any_client_ref": switch (request.getName()) { case "client_types": return ClientRefLoader.getClientTypes(ds); case "mailboxes": return MailboxesRefLoader.getMailboxes(ds); default: return ClientRefLoader.getClientReference(request.getName(), ds); } case "simple_ref": return ReferenceLoader.getReference(request, ds); } throw new IllegalStateException("  : " + request.getType()); } 

option with if

  public static List<String> getReference2(Request request, DataSource ds) { if ("enum_ref".equals(request.getType())) { return EnumRefLoader.getReference(ds); } if ("simple_ref".equals(request.getType())) { return ReferenceLoader.getReference(request, ds); } boolean selectByName = "client_ref".equals(request.getType()) || "any_client_ref".equals(request.getType()); if (!selectByName) { throw new IllegalStateException("  : " + request.getType()); } if ("client_types".equals(request.getName())) { return ClientRefLoader.getClientTypes(ds); } if ("mailboxes".equals(request.getName())) { return MailboxesRefLoader.getMailboxes(ds); } return ClientRefLoader.getClientReference(request.getName(), ds); } 

Total for me: 5 indents VS 2.

But again, who likes what option? Most would prefer getReference1.
Separately, it is worth noting that the number of indents still depends on the chosen code formatting style.

3. Check for null


If the switch statement is used with strings or enums, the selection parameter must be checked for null. Let's go back to the getTypes examples.

  // switch:   null  public List<String> getTypes1(Channel channel) { //   null if (channel == null) { throw new IllegalArgumentException("    "); } switch (channel) { case INTERNET: return Arrays.asList("SMS", "MAC"); case HOME_PHONE: case DEVICE_PHONE: return Arrays.asList("CHECK_LIST"); default: throw new IllegalArgumentException("   " + channel); } } // if:      null public List<String> getTypes2(Channel channel) { if (INTERNET == channel) { return Arrays.asList("SMS", "MAC"); } if (HOME_PHONE == channel || DEVICE_PHONE == channel) { return Arrays.asList("CHECK_LIST"); } throw new IllegalArgumentException("   " + channel); } 

Total for me: extra code.

Even if you are absolutely certain now that null "will not come", this absolutely does not mean that it will always be like this. I analyzed the corporate boottrack and found confirmation to this statement. To be fair, it should be noted that the code structure expressed through if is not without this problem, often constants for comparison are used on the right, not the left, for example, name.equals ("John"), instead of "John". Equals (name). But within this article, on this point, I wanted to say that, other things being equal, the approach with the switch is inflated by checking for null, if if the check is not needed. I will also add that static code analyzers easily cope with possible null-bugs.

4. heterogeneity


Very often, with long-term maintenance of the code, the code base swells up and you can easily find code like the following:

 public static void doSomeWork(Channel channel, String cond) { Logger log = getLogger(); //... switch (channel) { //... case INTERNET: //      if ("fix-price".equals(cond)) { // ... log.info("Your tariff"); return; } //   ? // ... break; //... } //... } 

Total for me: a different style.

There used to be a 'clean' switch, and now switch + if. There is, as I call it, a mixture of styles, a part of the 'choice' code is expressed through a switch, a part through an if. Of course, no one forbids the use of if and switch together, unless it is a matter of a select / sub select operation, as in the example above.

5. Whose break?


When using the switch operator, a case or a cycle may appear in case blocks, a switch in a cycle, with its own interruptions to the processing. The question is, whose break, gentlemen?

 public static List<String> whoseBreak(List<String> states) { List<String> results = new ArrayList<>(); for (String state : states) { Result result = process(state); switch (result.getCode()) { case "OK": if (result.hasId()) { results.add(result.getId()); //  ,  break -? break; } if (result.getInnerMessage() != null) { results.add(result.getInnerMessage()); //    continue; } // ... break; case "NOTHING": results.add("SKIP"); break; case "ERROR": results.add(result.getErroMessage()); break; default: throw new IllegalArgumentException(" : " + result.getCode()); } } return results; } 

Total for me: the readability of the code decreases.

To be honest, there were also much more complicated code examples.

6. Inappropriateness


In java 7, it became possible to use the switch statement with strings. When our company switched to java 7, it was a real Switch-Boom. Maybe for this, and maybe for another reason, but in many projects there are similar blanks:

 public String resolveType(String type) { switch (type) { case "Java 7 ?": return ""; default: throw new IllegalArgumentException(" switch, ,      case " + type); } } 

Total for me: inappropriate designs appear.

7. Glam switch to the accompaniment of hardcode


A little humor does not hurt.

 public class Hit { public static enum Variant { ZERO, ONE, TWO, THREE } public static void switchBanter(Variant variant) { int shift = 0; ZERO: ONE: while (variant != null) { shift++; switch (variant) { default: { THREE: { System.out.println("default"); break THREE; } break; } case ONE: { TWO: { THREE: for (int index = shift; index <= 4; index++) { System.out.println("one"); switch (index) { case 1: continue ONE; case 2: break TWO; case 3: continue THREE; case 4: break ZERO; } } } continue ONE; } case TWO: { TWO: { System.out.println("two"); if (variant == THREE) { continue; } break TWO; } break ZERO; } } variant = null; } } public static void main(String[] args) { switchBanter(ONE); } } 

No comments.

Conclusion


I do not urge you to abandon the switch operator, in places it is really good-looking and the noodles from if / if-else / else are porridge. But its excessive use wherever it gets, can cause discontent among other developers. And I'm one of them.

Separately, I wanted to note that from the point of view of understanding the code, I have no problems with switch / case - the meaning of what is written is clear, but from the point of view of perception of aesthetic beauty, it does.

And finally. Use what you like by dropping the imposed opinions by brackets, as long as your code is simple, working, beautiful and reliable. Good luck.

Source: https://habr.com/ru/post/320712/


All Articles