審美的な美しさ:Switch vs If

入門



開発者として、私たちは毎日コードに直面しており、私たちが好きで、見て、書いて、熱心になればなるほど、生産的で効果的になります。 私が言えることは、私たちは自分のコードを誇りに思っていることです。 しかし、1つのジレンマに悩まされます。2人の開発者が同じコードを見ると、まったく逆の感情を経験する可能性があります。 そして、その美的感覚に触発されたこれらの感情、感情が、あなたの周りのほとんどの専門家の感情と一致しない場合はどうでしょうか? 一般に、この話は、スイッチ構築の言語があまり好きではない理由についてです。 誰がこのホリバルナヤの位置に興味がありますか?



いくつかのニュアンス



以下では、switch文とJava言語でのその特定の実装とif文のこの構造の反対者の形式の比較について説明します。 誰もが、スイッチオペレーターが優れた能力を持っていることを知っています-パフォーマンス(まれな例外を除く)、そしてこの瞬間は考慮されません-それをカバーするものは何もないからです。 しかし、それでも何が問題なのでしょうか?



1.冗長性とかさ高さ



switchステートメントを使用することから私を遠ざけた理由の1つは、デザイン自体の冗長性と煩わしさです。switch、case、break、およびdefaultを確認し、それらの間におそらく戻り値とスローのある括弧を省略します。 Javaのサービスワードを使用したり、コード内で多数のサービスワードを使用したりしないように強くお勧めするとは思わないでください。いや、単純なことは冗長ではないと思います。 これが私が話していることの小さな例です:



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); } }
      
      





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); }
      
      





私のための合計:スイッチ/ケース/デフォルトVSの場合。



コードの好きな人を尋ねると、ほとんどの人が最初のオプションを好むでしょうが、私にとっては冗長です。 ここではコードのリファクタリングについて話しているのではなく、EnumMapやIdentityHashMapなどの定数を使用してチャンネルキーでリストを検索したり、チャンネル自体の必要なデータを削除したりすることもできますが、これは議論の余地のあるソリューションです。 しかし、戻って。



2.インデント



アカデミックな例ではswicth演算子を使用することができます-これはコードの唯一の部分ですが、処理しなければならないコードはより複雑で、チェック、ハッキング、およびどこか単なる主題の複雑さに覆われています。 そして、個人的には、そのような場所のすべてのインデントは迷惑です。 私たちは「生きている」例に行きます(過剰を取り除きましたが、本質は残っています)。



  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()); }
      
      





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); }
      
      





私の合計:5インデントVS 2。



しかし、再び、誰がどのオプションを好むのでしょうか? ほとんどはgetReference1を好みます。

それとは別に、インデントの数は、コードの選択されたフォーマットスタイルに依存することに注意してください。



3. nullを確認します



switchステートメントを文字列または列挙型で使用する場合、選択パラメーターのnullを確認する必要があります。 getTypesの例に戻ります。



  // 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); }
      
      





私のための合計:余分なコード。



たとえnullが来ないことを今でも絶対に確信していても、これは常にnullになるという意味ではありません。 私は企業のバグトラックを分析し、この声明の確認を見つけました。 公平には、ifで表現されたコード構造にはこの問題がないわけではないことに注意する価値があります。たとえば、「John」ではなくname.equals(「John」)のように、比較用の定数が右側で使用されます。 しかし、この記事のフレームワークでは、この点について、チェックが不要な場合はnullをチェックすることでスイッチアプローチが拡張されると言いたかったのです。 また、静的コードアナライザーは、考えられるヌルのバグに簡単に対処できることも付け加えます。



4.モトリーネス



非常に多くの場合、コードの長期メンテナンスにより、コードベースが膨らみ、次のようなコードを簡単に見つけることができます。



 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; //... } //... }
      
      





私の合計:異なるスタイル。



以前はクリーンなスイッチがありましたが、今は+ ifを切り替えます。 私が呼ぶように、スタイルの混合が発生し、「選択」コードの一部はスイッチを介して、一部はifを介して表現されます。 もちろん、上記の例のように、これがselect / sub select操作に適用されない場合、ifとswitchを一緒に使用することを禁止しません。



5.誰が壊れますか?



switchステートメントを使用すると、caseブロックまたは1回転でサイクルが表示される場合がありますが、処理の中断とともに、スイッチがサイクルで表示される場合があります。 問題は、誰の休憩、紳士ですか?



 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; }
      
      





私にとっては合計:コードの可読性が低下します。



正直に言うと、もっと複雑なコード例がありました。



6.不適切



Java 7では、文字列でswitchステートメントを使用できるようになりました。 当社がJava 7に切り替えたとき-それは本当のスイッチブームでした。 たぶんこれ、または別の理由かもしれませんが、多くのプロジェクトで同様の空白があります:



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





合計:不適切なデザインが表示されます。



7.ハードコードの伴奏への魅力的な切り替え



少しのユーモアは痛くない。



 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); } }
      
      





コメントはありません。



おわりに



switchステートメントを拒否することはお勧めしません。一部の場所では本当に見栄えが良く、if / if-else / elseの麺はまだ混乱しています。 しかし、どこでもそれを過度に使用すると、他の開発者の間で不満が生じる可能性があります。 そして、私もその一人です。



また、コードを理解する観点から、スイッチ/ケースに問題はありません-書かれた意味は明確ですが、美的感覚を知覚する観点からは-あります。



そして最後に。 課された意見を省略して、好きなものを使用してください。主なことは、コードがシンプルで、機能し、美しく、信頼できるということです。 すべて最高。



All Articles