web-dev-qa-db-ja.com

SwitchステートメントのリファクタリングとSwitchステートメントの実際の使用はありますか?

私はこれを読んでいました article と思っていましたが、すべてのスイッチステートメントをディクショナリまたはファクトリーに置き換えることですべてのスイッチステートメントを削除し、プロジェクトにスイッチステートメントがまったくないようにしますか?.

なかなか上手くいきませんでした。

問題は、switchステートメントに実際の用途があるか、それともディクショナリまたはファクトリーメソッドのいずれかで置き換えますか(ファクトリーメソッドを使用する場合、もちろん、オブジェクトを作成するためのswitchステートメントの最小限の使用法があります)。工場を使用して...しかしそれはそれについてです。

28
Kanini

switchステートメントとポリモーフィズムの両方が使用されます。ただし、3番目のオプション(関数ポインター/ラムダ、および高次関数をサポートする言語)も存在することに注意してください。問題の識別子をハンドラー関数にマッピングします。これは、たとえば、 OO言語ではないC、およびC#は*ですが、(まだ)Java OO =も*。

一部の手続き型言語(多態性も高次関数もなし)switch/if-elseステートメントは、ある種の問題を解決する唯一の方法でした。この考え方に慣れている多くの開発者は、OO言語では多形がより良い解決策である場合が多いとしても)switchを使い続けました。これがしばしば推奨される理由ですポリモーフィズムを支持してswitchステートメントを回避/リファクタリングするため。

とにかく、最良のソリューションは常にケースに依存します。問題は次のとおりです:長期的に見れば、どのオプションがよりクリーンで、より簡潔で、より保守可能なコードですか?

多くの場合、switchステートメントは扱いにくくなり、数十のケースがあり、メンテナンスが困難になります。それらを単一の関数に保持する必要があるため、その関数は非常に大きくなる可能性があります。これが事実である場合、マップベースおよび/またはポリモーフィックソリューションへのリファクタリングを検討する必要があります。

同じswitchが複数の場所でポップアップし始める場合、これらのすべてのケースを統合してコードを簡略化するには、多態性がおそらく最良のオプションです。特に、今後さらにケースが追加されることが予想される場合。毎回更新する必要がある場所が多いほど、エラーの可能性が高くなります。ただし、多くの場合、個々のケースハンドラーは非常に単純であるか、非常に多く存在するか、相互に関連しているため、完全な多態性クラス階層にリファクタリングするのはやり過ぎであるか、コードが重複したり、複雑になったりします。クラス階層を維持するのが難しい。この場合、代わりに関数/ラムダを使用する方が簡単な場合があります(言語で許可されている場合)。

ただし、switchが1か所にあり、いくつかのケースで単純なことを行っている場合は、そのままにするのが最善の解決策になる場合があります。

*ここでは「OO」という用語を大まかに使用しています。 「実際の」または「純粋な」オブジェクト指向オブジェクトについての概念的な議論には興味がありません。

44
Péter Török

これが恐竜に戻るところです...

Switchステートメント自体は悪くありません。問題となっているのは、switchステートメントの使用です。

最も明白なのは、コードを通じて何度も何度も繰り返される「同じ」switchステートメントです(ここで実行すると、再度実行しないようにあらゆる努力を払います)。そして、この後者のケースでは、対処できる可能性があります。ポリモーフィズムを使用して。通常、ネストされたケースにもかなり恐ろしいものがあります(私は以前は絶対的なモンスターを持っていました-私が「より良い」以外にどう対処するかは完全にはわかりません)。

スイッチとしての辞書の方が難しいと思います。スイッチがケースの100%をカバーしている場合は基本的にそうですが、デフォルトまたはアクションなしのケースが必要な場合は、少し面白くなります。

繰り返しを避け、オブジェクトグラフを適切な場所に作成していることを確認することが問題だと思います。

しかし、理解(保守性)の議論もあり、これは両方の方法を削減します-すべてがどのように機能するか(パターンとそれが実装されているアプリ)を理解すると、簡単です...しかし、1行のコードにアクセスすると新しい何かを追加する必要があり、追加/変更する必要があるものを見つけるために、あちこちを飛び越えなければなりません。

私たちの開発環境は非常に優れているので、紙に印刷されたコード(まるでそれがあったかのように)を理解できることが望ましいと感じています。指でコードを追跡できますか?私は実際にはそうではないことを受け入れます。今日、多くの良い習慣があると、それを行うことはできません。正当な理由があるためです。つまり、コードに慣れるのは難しいでしょう(あるいは、私は年をとっています...)。

14
Murph

Switch-statements vs subtype-polymorphismは古い問題であり、FPコミュニティで Expression Problem に関する議論で言及されていることがよくあります。

基本的に、型(クラス)と関数(メソッド)があります。新しい型や新しいメソッドを後から簡単に追加できるように、コードをどのようにコーディングしますか?

OOスタイルでプログラムする場合、新しいメソッドを追加することは困難です(既存のすべてのクラスをリファクタリングすることになるためです)が、以前と同じメソッドを使用する新しいクラスを追加するのは非常に簡単です。 。

一方、switchステートメント(またはOO同等、オブザーバーパターン))を使用する場合、新しい関数を追加するのは非常に簡単ですが、新しいケース/クラスを追加するのは困難です。

両方向に優れた拡張性を持たせるのは簡単ではないので、コードを記述するときは、どちらに拡張する可能性が高いかに応じて、ポリモーフィズムを使用するか、ステートメントを切り替えるかを決定します。

9
hugomg
私たちのプロジェクトにスイッチステートメントがまったくないように、それらをディクショナリまたはファクトリに置き換えることによって、すべてのスイッチステートメントを削除しますか?.

いいえ、そのような絶対的なものはめったに良い考えではありません。

多くの場所で、dictionary/lookup/factory/polymorphicディスパッチは、switchステートメントよりも優れたデザインを提供しますが、それでもディクショナリを入力する必要があります。場合によっては、実際に何が行われているのかがわかりにくくなり、switchステートメントを単にインラインにしたほうが読みやすく、保守しやすくなります。

4
Telastyn

これが言語にとらわれない方法であると考えると、フォールスルーコードはswitchステートメントで最もよく機能します。

switch(something) {
   case 1:
      foo();
   case 2:
      bar();
      baz();
      break;

   case 3:
      bang();
   default:
      bizzap();
      break;
}

ifと同等で、veryのデフォルトの扱いにくいケースがあります。また、フォールスルーが多いほど、条件リストが長くなることに注意してください。

if (1 == something) {
   foo();
}
if (1 == something || 2 == something) {
   bar();
   baz();
}
if (3 == something) {
   bang();
}
if (1 != something && 2 != something) {
   bizzap();
}

(しかし、他の回答のいくつかが言っていることを考えると、私は質問のポイントを逃したように感じます...)

2
Izkata

ケースの区別としてのswitchステートメントは実際に使用されます。関数型プログラミング言語は、パターンマッチングと呼ばれるものを使用します。これにより、入力に応じて異なる方法で関数を定義できます。ただし、オブジェクト指向言語では、ポリモーフィズムを使用することにより、同じ目標をよりエレガントに達成できます。メソッドを呼び出すだけで、オブジェクトの実際のタイプに応じて、対応するメソッドの実装が実行されます。これが、switchステートメントがコードのにおいであるという考えの背後にある理由です。ただし、OO言語の場合でも、抽象ファクトリパターンを実装するのに役立つ場合があります。

tl; dr-それらは便利ですが、多くの場合、もっと上手にできます。

1
scarfridge