変数を再利用する必要がありますか?


59

変数を再利用する必要がありますか?

私は多くのベストプラクティスがあなたがそれをするべきではないと言うことを知っていますが、後で、異なる開発者がコードをデバッグしていて、似たような3つの変数を持っているとき、唯一の違いはそれらがコードの異なる場所で作成されることです混乱した。単体テストは、この好例です。

しかし、私はないのベストプラクティスは、それに対して、ほとんどの時間であることを知っています。例えば、彼らはメソッドのパラメーターを「オーバーライドしない」と言います。

ベストプラクティスは、以前の変数をnullにすることに対してです(Javaにはnull、変数に代入するときに警告を出すSonarがあります。Java6 以降、ガベージコレクターを呼び出すためにそれを行う必要はありません。どの警告がオフになります;ほとんどの場合、デフォルトはオンです。)


11
Java 6以降?Javaのどのバージョンでも、変数にnullを割り当てる必要はありません。変数がスコープ外になると、そのオブジェクトへの参照を解放します。
ケビン・パン粉

35
変数の再利用は、コード難読化ツールが最初に使用するものの1つです
ルルーシュランペルージ

7
変数の再利用は、変数の適切な識別子の選択と競合します。現代の言語では、優れた識別子を持つことの利点を上回る、変数の再利用に対する利点は実際にはありません。
ジョレン

4
また、すべての再利用が再利用ではないことに注意してください。古いFORTRANプログラマーは、他の言語に流された人でさえ、すべてのDOループ(for-にI、J、K、L、M、N(またはi、j、k、l、m、n)を日常的に使用します。ループ)カウンター。SUM = SUM + A(I、K)* B(K、J)、またはsum + = a [i] [k] * b [k] [j];のようなものを見ることに慣れています。
ジョンR.ストローム

1
非常に限られたリソース(数キロバイトのRAM)でマイクロコントローラーをプログラミングする場合、変数の再利用は正当化される可能性があります。
m。アリン

回答:


130

問題が発生するのは、メソッドが長く、複数のタスクを連続して実行している場合のみです。これにより、コード自体を理解するのが難しくなります(したがって、保守が難しくなります)。変数を再利用すると、これに加えてリスクの余分な要素が追加され、コードの追跡がさらに難しくなり、エラーが発生しやすくなります。

IMOのベストプラクティスは、1つのことだけを行う短い方法を使用して、問題全体を排除することです。


単体テストはどうですか?たとえば、メソッドを2回実行した後、期待どおりに動作するかどうかをテストする必要があります。
IAdapter

20
@IAdapter、ユニットテストコードは別の問題であり、標準は生産コードよりも緩和される可能性があります。それでも、読みやすく維持しやすいものにすることは優先度が高いです。ユニットテストからメソッド抽出することもできます(また、そうする必要があります)。
ペテルトレック

3
@IAdapter、あなたが説明したシナリオでは、変数を再利用しません。適切なのはresult1 = foo(); result2 = foo(); Assert.AreEqual(result1, result2);、この場合に変数を再利用する必要がある場所があまりないようなものです。
JSBはձոգչ

1
@IAdapter for(int i = 0; i <2; i ++){result = foo(); assertEquals(expectedResult、result);}
ビルK

2
+1-これは、より小さなメソッドを作成する場合に適したコード臭です。

49

メソッドでの変数の再利用は、リファクタリング/分割する必要があることを示す強力な兆候です。

したがって、私の答えは、それらを再利用するべきではないということです。そうすれば、後でリファクタリングするのがずっと難しくなるからです。


19

場合によります。

一部の変数は、特定の種類のデータを保持する目的で作成される場合があり、関数の実行中に変更される可能性があります。ここで、リターンコードが思い浮かびます。例:

void my_function() {
    HRESULT errorcode;
    errorcode = ::SomeWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
    errorcode = ::AnotherWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
}

変数名は、この変数が何を保存するかを非常に明確にします。変数は概念的には、機能の過程で非常に類似したものの異なるインスタンスによって論理的に使用されるコンテナである、このような他のユースケースも考えられます。

ただし、一般的に、これはコードの可能な読者に絶対に明らかな状況でのみ行われるべきです。コードの読みやすさに関係なく極端な最適化を行う場合を除いて、型が適合するという理由だけで変数を再利用する必要はありません。

これらはすべて、基本的に変数の命名に関する優れた慣行に基づいています。名前は自分で話す必要があります。すべての再利用の正確な目的を短い名前にすることが難しい場合は、個別の変数を使用するのが最善です。


15

変数を(特に単体テストで)再利用しない最大の理由は、テストとデバッグが難しい不必要なコードパスが導入されるためです。優れた単体テストは他のテストから独立している必要があり、単体テストフィクスチャでクラス(インスタンス)レベルの変数を再利用する場合、各テストの前にそれらの状態をアサートする必要があります。優れた単体テストは欠陥も隔離するため、理論上、各テストケース(メソッド)はテスト対象のシステムに対して1つの動作のみをアサートする必要があります。テストメソッドがこのように記述されている場合、メソッドレベル変数を再利用する必要性や利点はほとんどありません。最後に、クロージャーと非同期処理をサポートする言語では、メソッド全体で変数を再利用している場合、一体何が起こっているのかを推論するのは非常に困難です。


だから私は-testSomethingのようなテストメソッドを書き、単一のメソッド呼び出しのためにアサートする必要があります-testSomethingTwoInvocationsと2番目の呼び出しのためだけにアサートしますか?
IAdapter

2
はい。失敗したのが最初の反復か2回目の反復かを特定するのに役立ちます。mspecはこれに役立つかもしれませんが、その継承ツリーは非常に便利です。
ブライアンベッチャー

「クラス変数」または「インスタンス変数」を意味するときに「変数」を使用する場合は、より明確に自分自身を表現する必要があります。
gnasher729

13

異なる変数を使用する必要があります。同僚が混乱することを心配している場合は、同僚の役割を明確にカバーする名前を付けてください。
変数の再利用は、将来の混乱の原因となる可能性があります。今それをクリアする方が良い。
場合によっては、同じ変数名を再利用できます。たとえばi、単純なカウントループで。これらの場合、もちろん、変数が独自のスコープ内にあることを確認する必要があります。

編集:変数を再利用することは、単一責任原則に違反していることを示している場合があります。再使用された変数が同じ役割で使用されているかどうかを確認してください。もしそうなら、それはまったく再利用されないかもしれません(ただし、上記の変数の範囲を制限するために、2つの異なる変数を持つことが依然として好ましいかもしれません)。異なる役割で使用される場合、SRP違反が発生します。


4

他の回答から得られたすばらしいアドバイスに関係なく変数を再利用したい場合が1つあります。それは、コンパイラーに支援が必要な場合です。

場合によっては、特定のレジスター割り当て変数がコードの次の部分で使用されなくなっていることをコンパイラーが十分に理解できないことがあります。そのため、理論的には次の変数に空きレジスタを再利用せず、生成されたコードは次善の可能性があります。

この状況を正しくキャッチできない現在のメインストリームコンパイラは知らないので、コンパイラが最適でないコードを生成しているという事実を知らない限り、決してこれを実行しないでください。カスタムコンパイラを使用して特別な組み込みシステム用にコンパイルしている場合、この問題が発生する可能性があります。


17
-1。これが実際に発生する現実の状況を指し示すことができず、変数を再利用するとかなりの違いが生じる場合を除きます。これは理論的な問題に対する理論的な解決策であり、その点ではあまり良い解決策ではありません。コンパイラが変数を配置することを決定する場所は、コンパイラの関心事です。コンパイラが貧弱なコードを生成し、それが本当に違いを生む場合は、コンパイラを修正または交換します。
カレブ

3
@Caleb-開発しているプラ​​ットフォーム、特に独自の組み込みプラットフォームのコンパイラソースコードに常にアクセスできるとは限りません。私はまた、私の答えで、私が見てきたすべてのケースでこの活性分析を正しく実行しない現在の主流コンパイラ(または実際にはインタプリタ/ VM)を知らないとDIDしました。しかし、私過去(10年ほど前)に非常に必要だったカスタムコンパイラを使用して、独自の組み込みシステムに取り組んでました。システムの機能は非常に限られており、コンパイラも同様であったため、この状況はそこで発生しました。
ジョリスティマーマンズ

2
+1過去のプロジェクト(ANSI Cで記述された高度に最適化されたメディアコード)でまさにそのような再利用を行いました。私が覚えている限り、その違いはアセンブリで簡単に確認でき、ベンチマークで測定できました。Javaでこのような再利用を行う必要が決してないことを願っています。
-gnat

@Calebのコンパイラーの修正または交換は、いくつかの理由でオプションではない場合があり、単にあなたが持っているもので間に合わせる必要があります。

@ThorbjørnRavnAndersenはそれが考えられる 1はお粗末なコンパイラを使用することを余儀なくされる可能性がありますし、さらにそのパフォーマンスは2番目のコンパイラを推測し、レジスタを再利用するために、あなたのコードを操作する必要があるので重要であるかもしれないということ?はい。それがある可能性が高いですか?いいえ。MadKeithVは、実世界の例を提供できないことに同意します。それはベストプラクティス?それは良いスタイルコード品質の指標ですか?絶対にありません。これは書かれた質問に対する有用な回答ですか?MadKeithVに敬意を表しますが、そうではないと思います。
カレブ

2

私はノーと言うでしょう。

このシナリオについて考えてみましょう。プログラムがクラッシュし、コアダンプを検査して何が起こったのかを把握する必要があります...違いを確認できますか?;-)


コアダンプが最適化されたビルドからのものである場合、変数がメモリを共有している可能性があります。だから、それは役に立たないかもしれません。
ウィンストンイーバート

もちろん、最適化されたビルドはデバッグにはあまり役立ちません!しかし、いずれにしてもデバッグビルドを使用するオプションがまだあります...そして、デバッガをアタッチしている場合、変数がどのように変化するかを見るために関数の先頭からステップごとに進む必要はありません。しないでください!:-D
FORTRAN

2

いいえ、マシンが実行しているコード(...アセンブリコード)を改善しているわけではありません。コンパイラーが変数に使用するメモリーはすべてコンパイラーに再利用してください。かなり頻繁にレジスターになり、再利用しても何も買えませんでした。コードを読みやすくすることに焦点を当てます。


0

場合によります。型が変数ではなく値に存在する動的言語では、たとえば文字列である関数への適切な名前の引数がある場合。アルゴリズムの変更は、整数として解釈された後に常に使用されることを意味し、最初のドラフトとして、

scrote = int(scrote)

しかし、最終的には、関数に送信される型を変更し、パラメーターの型の変更に注意します。


1
コードの長いバッチの途中に「scrote = int(scrote)」のような行を置くことは、メンテナンスプログラマーを夢中にさせる素晴らしい方法です。
11

問題は、あなたが言及した「長いコードのバッチ」にある可能性が高いです。
Paddy3118

受け入れられている回答のメモにあるように、変数の再利用は実際には長いコードのバッチでのみ問題になります。基本的に、 "scrote = int(scrote)"のような何かを書くような状況では、新しい変数にint(scrote)を入れるか、int(scrote)を新しい関数に渡すほうがよいでしょう。
ジョッキング

0

まず、開発環境を見てください。同じスコープの異なるスコープに5つの変数があるためにデバッグに問題があり、デバッガーがこれらの変数のどれが必要かを表示しない場合、同じ名前の5つの変数を使用しないでください。これを実現するには、2つの方法があります。1つの変数を使用するか、5つの異なる名前を使用します。

デバッガーは、多くの変数を持つ関数をデバッグするのも苦痛かもしれません。20個の変数を持つ関数が16個の変数を持つ関数よりもデバッグが難しい場合は、5つの変数を1つの変数に置き換えることを検討できます。(「考慮する」は「常に」と同じではありません)。

変数の目的が常に同じである限り、1つの変数を複数の場所で使用しても構いません。たとえば、10回の関数呼び出しがエラーコードを返す場合、呼び出しごとにすぐに処理され、10ではなく1つの変数を使用します。ただし、まったく異なることに同じ変数を使用しないでください。顧客名に「名前」を使用し、10行後に会社名に同じ変数を使用するように、それは悪いことであり、トラブルに巻き込まれます。さらに悪いことに、顧客名に「customerName」を使用し、10行後に会社名に同じ変数「customerName」を使用しています。

重要なのは、鉄則ではないことです。すべてには長所と短所があります。「ベストプラクティス」が1つのことを示唆しており、特定の状況ではそれが悪い考えだと言う理由がある場合は、それをしないでください。


0

まず、開発環境を見てください。同じスコープの異なるスコープに5つの変数があるためにデバッグに問題があり、デバッガーがこれらの変数のどれが必要かを表示しない場合、同じ名前の5つの変数を使用しないでください。これを実現するには、2つの方法があります。1つの変数を使用するか、5つの異なる名前を使用します。

デバッガーは、多くの変数を持つ関数をデバッグするのも苦痛かもしれません。20個の変数を持つ関数が16個の変数を持つ関数よりもデバッグが難しい場合は、5つの変数を1つの変数に置き換えることを検討できます。(「考慮する」は「常に」と同じではありません)。

変数の目的が常に同じである限り、1つの変数を複数の場所で使用しても構いません。たとえば、10回の関数呼び出しがエラーコードを返し、呼び出しごとにすぐに処理される場合は、10ではなく1つの変数を使用します。これには1つの問題があります。しかし、ここでは、呼び出し6の後にエラーコードを読み取ったが、変数が呼び出し5の後に実際に変更されていない場合、コンパイラは警告を表示せずに無駄なデータを取得します。変数は初期化されているため、データは今では役に立たない。

重要なのは、鉄則ではないことです。すべてには長所と短所があります。「ベストプラクティス」が1つのことを示唆しており、特定の状況ではそれが悪い考えだと言う理由がある場合は、それをしないでください。


-2

状態を維持したり、定数を保存したりする必要がある場合は、グローバル変数を使用します。変数を再利用することにより、メモリ内の場所を指す名前のみを再利用できます。初期化の説明的な名前は、明快さだけを得ることができます(Javaを想定し、プリミティブOCDを想定しない)。

手でコードを難読化したり、他の開発者をいらいらさせたりしない限り。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.