アプリケーションプログラミングにおけるGOTO



思考する



GOTOに関する「一般化された」記事と、 システムプログラミングにおける GOTOに関する記事に基づきます



GOTOとその代替手段を使用する動機は、システムプログラミングとアプリケーションプログラミングでは根本的に異なります。これも、ホリバーの重要な理由です。 状況を明確にするために、適用プログラミングのコンテキストでのみGOTOを検討します。



要点 :アプリケーションプログラミングでは、GOTOを回避する方が確実に優れています。



私たちが証明するように

  1. アプリケーションプログラミングでは、コードの1つのパラメーターである保守性が重要です。
  2. 後藤は、ごく一部の場合にのみ明確に伴奏を劣化させず、これらの場合でさえ、基本的に他の選択肢と変わらない
  3. 少数のケースのために、それを使用することは有害です:

    1)非常に低レベルであるため、プログラマーを多く破損します(他の場所で使用する誘惑があります)-GOTOを適用できるケースの割合が少ないため、多くの害があります。

    2)そのような場合でも、より美しい代替手段があります。






GOTO-プロパティとコード品質への影響



コード品質オプション
(コードが正しく記述され、すべての異常な状況が考慮されると信じています):

  1. リソース(メモリ、プロセッササイクル)の消費は、「マシンから」の優先事項です。
  2. コードの保守性(保守の容易さ)は、「人から」の優先事項です。

    1)コードの可読性-記述されたコードを理解するのがどれだけ簡単か(したがって、記述とデバッグがどれだけ簡単か)

    2)変更中のエラー耐性-ミスを犯すのがどれほど難しいか/コードを変更するときに気づくのがどれだけ簡単か、

    3)共通標準のサポートの容易さ-コードの記述がプログラマに特定の一般標準からの逸脱を教える限り
GOTOの一般的なプロパティ:
  1. 非構造化 :ほとんど任意の場所に挿入でき、他のブランチデザインとは異なり、そこにどのように到達したかを理解するのは困難です。
  2. ソースコードをスレーブ化します :構造化ブロックをさまざまな方法で変更できる場合、コンストラクターのように順序を並べ替える場合、GOTOはコンストラクターのいくつかのブロックを接続する爪です-実装後、コードを変更するのは非常に困難です


GOTOを使用すると、コード品質パラメーターとそのコンポーネントの両方に影響します。 同時に、アルゴリズムの複雑さを決して変えないので、GOTOリソースの消費が埋め込まれる小さな定数によって減少することは明らかです。



GOTOではないもの:
  1. 他の実行フロー制御構造-if、switch、whileなど:すべてのフローブランチは構文によってハードコードされ、同じブロックの境界に配置されます-上部または下部(リターンの場合-関数の境界)、およびGOTOは任意に配置できます
  2. 自動的に生成されたコード-生成されたアセンブラのように、コードを掘り下げてサポートする必要はありません。


アプリケーションプログラミングのGOTO機能



ここで適用されるプログラミングは、Java、C#、C ++、インタープリター言語などの例外処理への構造的アプローチを含む、コード構造化をサポートする高レベル言語でのプログラミングです。 -一般に、標準が主流に適用されました。 私はCを低レベル言語とは考えておらず、現在は主にシステムプログラミングに使用されています。



適用プログラミングの機能:
  1. ポイントの最適化の必要はありません-クロックサイクルまたはメモリセルを分離するため、GOTOの考慮からリソースの節約を破棄できます-保守性のみが残ります
  2. 好きなようにロジックを構造化することができます-好きなようにそれを関数/メソッドに組み合わせたり、好きなだけ多くの変数、クラスなどを持つことができます 任意の名前で、例外をスローする機能
GOTOはコードの保守性を低下させるだけです


システムプログラミングでは、リソースを最大限に節約することが重要であるため、おそらく、この目的でGOTOを使用することが正当化されます。

また、アプリケーションプログラミングでは、「リソース消費」パラメーターは破棄できますが、保守性パラメーターのみが残り、GOTOが低下します。





GOTO-問題と修正



コード構造とその代替を移動するためのさまざまなオプションでGOTOを使用することを検討してください。



1.外部からブロックへの入り口:


1.1「not cycle」への入り口:
GOTOを使用しない簡単で明白な対応:



if(a) GOTO InsideIf; if(b) { foo(); InsideIf: bar(); }
      
      





=>



  if(b) { foo(); } if(a || b) { bar(); }
      
      





1.2サイクルへの参加:
不可能:実行フローはまったく明確ではありません:



  if(goIntoCycle) GOTO MiddleOfCycle; for(...) { foo(); MiddleOfCycle: bar(); }
      
      







2. 1つのブロック内の遷移:
必要なし、書くのは簡単、通常if / else:



  if(bNotNeeded) GOTO startFromC: B(); startFromC: C();
      
      





=>



  if(bNeeded) { B(); } C();
      
      







3.ブロックから外へ出る


これは、GOTOを使用する主なケースです。 さらに小さなものに分割し、詳細な例を検討します。

一般的なアプローチ - 最大は分解されますと呼ばれるスピーカーとのフラグにロジックを修正、の意味内のメソッドに分割 - 読みやすく、自己文書化コードを取得します。



重要なルール:
  1. 除外を使用しないでください



    1)私たちは常に例外を使用してエラーや緊急事態を処理します。したがって、目障りにならないように他の目的には使用しません。



    2)ネストの内部レベルから例外を誤って「飲み込む」ことができます。



    3)それは高価な喜びです -アプリケーションプログラミングでは不正をすることは価値がありませんが、単純な代替手段の存在下でリソースを分散させることは意味がありません。
  2. 特にネストの異なるレベルからの複数のリターンを回避します 。それ以外の場合、GOTOとの違いはほとんどありません。ほとんどの場合、コードの可読性も困難です。 (おそらく、これには別の正当化が必要です-彼はホリバーでもあります)。


3.1。 ネストの1つのレベルから抜け出す唯一の方法:
if / breakなどで簡単に置き換えられます。



3.2。 ネストの1つのレベルからのいくつかの出口:


3.2.1エラー処理-例外のみを使用
(これが明白であることを望みます;そうでない場合は、別の記事で説明できます)



3.2.2オプションの列挙-たとえば、次の場合


  class Tamagochi { function recreate() { if(wannaEat) { eat(); GOTO Cleanup; } if (wannaDrink) { drink(); GOTO Cleanup; } if(wannaDance) { Dance(); } return HAPPY; //true Cleanup: return washHands() && cleanKitchen(); } }
      
      





問題 (常に固有のGOTO実行スレッドの非自明性を除く):

彼らはwannaEatとwannaDanceの場合に睡眠行動を追加したかった-すべて、wannaEatとwannaDrinkの一般化は破壊された。



美しくする方法 (即時拡張バージョン):



  function recreate() { if(wannaEat) { eat(); needCleanup = true; needSleep = true; } if (wannaDrink) { drink(); needCleanup = true; } if(wannaDance) { Dance(); needSleep = true; } result = HAPPY; //true if(needCleanup) result = result && washHands() && cleanKitchen(); if(needSleep) result = result && sleep(); return result; }
      
      







3.3。 いくつかのレベルのネストを終了します。


3.3.1異なるロジック(異なる責任)を簡単に区別できる場合:


  class BatchInputProcessor { function processInput(inputRecords) { for (inputRecord in inputRecords) { for (validator in validators) { if (!validator.check(inputRecord) GOTO ValidationFailed; } item = new Item(); for (fieldValue in inputRecord.fieldValues) { setFieldValue(fieldValue.field, fieldValue.value); } itemsToStore.add(item); } return store(itemsToStore); ValidationFailed: log(failedValidation); tryToCorrect(inputRecords); ... } }
      
      





=>



  function processInput(inputRecords) { allRecordsAreValid = true; for(inputRecord in inputRecords) { recordIsValid = validateRecord(inputRecord, validators); if(!recordIsValid) { allRecordsAreValid = false; break; } else { itemToStore = createItemFromRecord(inputRecord); itemsToStore.add(item); } } if(allRecordsAreValid) { result = store(itemsToStore); } else { log(failedValidation); tryToCorrect(inputRecords); ... } }
      
      







3.3.2。異なるロジックを区別するのがより困難であるか、コードがより複雑になります。


原則として、これは同じタイプのネストされたループの場合です。



  function findOrCreateTriadaOfEqual(firstArray, secondArray, thirdArray) { result = null; for(first in firstArray) { for(second in secondArray) { for(third in thirdArray) { if(first == second && second == third) { result = array(first, second, third); GOTO Found: } } } } equal = new Item(); result = array(equal, equal, equal); Found: log(result); return result; }
      
      







まだ多くのオプションがあります:

  1. 内側のループから戻る別のサブ関数に入れます-最も簡単な
  2. 一般化 -再帰関数型findEqualInArrays(arrayOfArrays、currentArrayIndex、currentFoundItemsArray)を作ります。
  3. 「(結果)ブレーク」が最も不器用です:
  result = null; for(first in firstArray) { for(second in secondArray) { for(third in thirdArray) { if(first == second && second == third) { result = array(first, second, third); break; } } if(result) break; } if(result) break; }
      
      







これはGOTOよりも悪く見える唯一のオプションであり、GOTOはさらに明確です。 しかし、ほとんどの場合、他のオプションがあります。



「GOTO»なし! - 他のオプションなし例残り無視できるほど小さな割合について、あなたはちょうどあなたがまだ少なくとも旗を作ることができますが、ガイドラインが容易になりますかを決定する必要があります。



要約:



最も重要なことはメンテナンスです。



GOTOは常に保守性を損なうため



GOTOなしで行う必要があります -十分な標準ツール:

  1. 例外によるエラー処理。
  2. 分解-多くの問題を解決する大きな方法は、個々のタスクを解決する小さなものに分割されます。
  3. 名前を話す変数のロジック(計算された条件と式)を修正します。



All Articles