コピーアンドペーストコーディングはこれまでに受け入れられますか?

StackOverflow https://stackoverflow.com/questions/401856

  •  03-07-2019
  •  | 
  •  

質問

コピーアンドペーストプログラミングは悪いアイデアであると一般に受け入れられていますが、実際には実行する必要がある2つの関数またはコードブロックがある状況を処理する最良の方法はいくつかの方法でそれらを一般化するのは非常に面倒ですか?

いくつかの小さなバリエーションを除いてコードが実質的に同じであるが、それらのわずかなバリエーションは、パラメーター、テンプレートメソッド、またはそのようなものを追加することによって簡単に除外できるものではない場合はどうなりますか?

より一般的には、コピーアンドペーストの小さなコーディングが本当に正当化されたことを認めるような状況に遭遇したことがありますか。

役に立ちましたか?

解決

3箇所以上でコードを使用しない限り、抽象化は報われないため、一度コピーして貼り付ける(コードの重複は最大2つのインスタンスに制限する)と言われています。 ()私は、必要に応じてすぐにリファクタリングの良い習慣にしようとしています。

他のヒント

機能についてこの質問をする

"この小さな要件が変更された場合、それを満たすために両方の機能を変更する必要がありますか?"

もちろん、許容される場合もあります。そのため、人々はスニペットファイルを保持しています。ただし、コードを頻繁にカットアンドペーストする場合、または数行以上を使用する場合は、コードをサブルーチンにすることを検討する必要があります。どうして?オッズは何かを変更する必要があるため、このように変更する必要があるのは一度だけです。

中程度の場合は、使用可能なマクロがある場合に使用します。

はい、あなたの言うとおりです。マイナーだがファクタリングが難しいバリエーション。状況が本当に必要とするものである場合、自分自身を鞭打ちにしないでください。

Reカットアンドペーストは許容されます:

はい。セグメントがわずかに異なっていて、使い捨てシステム(非常に短い時間存在し、メンテナンスの必要がないシステム)を実行している場合。それ以外の場合は、通常、共通点を抽出する方が適切です。

似ているが正確に似ていないセグメントを再表示:

データに違いがある場合は、関数を抽出し、データの違いをパラメーターとして使用してリファクタリングします(パラメーターとして渡すデータが多すぎる場合は、オブジェクトまたは構造にグループ化することを検討してください)。 関数の何らかのプロセスに違いがある場合は、コマンドパターンまたは抽象テンプレートを使用してリファクタリングします。これらのデザインパターンを使用してもリファクタリングが依然として難しい場合は、関数が独自に多くの責任を処理しようとしている可能性があります。

たとえば、diff#1とdiff#2の2つのセグメントが異なるコードセグメントがある場合。また、diff#1ではdiff1Aまたはdiff1Bを使用でき、diff#2ではdiff2Aおよびdiff2Bを使用できます。

diff1Aの場合& diff2Aは常に一緒で、diff1B& diff2Bは常に一緒になり、diff1A& diff2Aは、1つのコマンドクラスまたは1つの抽象テンプレート実装に含めることができ、diff1B&別のdiff2B。

ただし、複数の組み合わせ(つまり、diff1A& diff2A、diff1A& diff2B、diff1B& diff2A、diff1B& diff2B)がある場合は、処理が多すぎる可能性があるため、関数を再考する必要があります単独で責任を負います。

Re SQLステートメント:

ロジック(if-else、loops)を使用してSQLを動的に構築すると、可読性が犠牲になります。ただし、すべてのSQLバリエーションを作成することは維持が困難です。そのため、途中で会ってSQLセグメントを使用します。 共通点をSQLセグメントとして抽出し、それらのSQLセグメントを定数として使用してすべてのSQLバリエーションを作成します。

例:

private static final String EMPLOYEE_COLUMNS = " id, fName, lName, status";

private static final String EMPLOYEE_TABLE = " employee";

private static final String EMPLOYEE_HAS_ACTIVE_STATUS = " employee";

private static final String GET_EMPLOYEE_BY_STATUS =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + EMPLOYEE_HAS_ACTIVE_STATUS;

private static final String GET_EMPLOYEE_BY_SOMETHING_ELSE =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + SOMETHING_ELSE;

私の会社のコードベースには、共通性の高い一連の約10個程度の大きなSQLステートメントがあります。すべてのステートメントには、共通のコア、または少なくとも1つまたは2つの単語だけが異なるコアがあります。次に、10個のステートメントを3つまたは4つのグループにグループ化し、コアに共通の付属物を追加します。この場合も、各付属物で1つまたは2つの単語が異なります。とにかく、10個のSQLステートメントは、大幅に重複したベン図のセットと考えてください。

重複を避けるような方法でこれらのステートメントをコーディングすることを選択しました。そのため、ステートメントを作成する関数(技術的にはJavaメソッド)があります。共通コアの単語の違いを説明するいくつかのパラメーターを取ります。次に、付属物を構築するためのファンクターが必要になります。これはもちろん、小さな違いの場合はより多くのパラメーターで、付属物の場合はより多くのファンクターでパラメーター化されます。

このコードは、SQLが繰り返されないという点で賢いです。 SQLの句を変更する必要がある場合は、1か所で変更し、10個すべてのSQLステートメントがそれに応じて変更されます。

しかし、人は読みにくいコードです。特定のケースでどのSQLが実行されるかを判断する唯一の方法は、デバッガーをステップスルーし、完全にアセンブルされた後にSQLを出力することです。また、句を生成する特定の関数がどのように大きな図に適合するかを理解することは厄介です。

これを書いて以来、SQLクエリを10回カットアンドペーストするだけでよいのではないかとよく疑問に思いました。もちろん、これを行った場合、SQLへの変更は10箇所で発生する可能性がありますが、コメントは更新する10箇所を示すのに役立ちます。

SQLを理解しやすくし、すべてを1か所にまとめることの利点は、SQLをカットアンドペーストすることの欠点をおそらく上回るでしょう。

マーティン・ファウラーが示唆するように、

一度だけやればいい、

2回繰り返して、臭いがし始めます。

3回行い、リファクタリングを行います。


編集:コメントへの回答で、アドバイスの起源はドン・ロバーツです:

3回ストライクし、リファクタリングします

Martin Fowlerは、リファクタリングの第2章、3つの規則(58ページ)のセクションで説明しています。

絶対に必要ない..

:)

問題のコードを投稿すると、見た目よりも簡単であることがわかります

それがそれを行う唯一の方法である場合、それを実行します。多くの場合(言語に応じて)、オプションの引数を使用して同じ関数に小さな変更を加えることができます。

最近、PHPスクリプトにadd()関数とedit()関数がありました。どちらも実質的に同じことを行いましたが、edit()関数はINSERTクエリではなくUPDATEクエリを実行しました。次のようなことをしました

function add($title, $content, $edit = false)
{
    # ...
    $sql = edit ? "UPDATE ..." : "INSERT ...";
    mysql_unbuffered_query($sql);
}

うまくいきました-しかし、コピー/貼り付けが必要な場合もあります。それを防ぐために奇妙で複雑なパスを使用しないでください。

  1. 良いコードは再利用可能なコードです。
  2. 車輪を再発明しないでください。
  3. 例が存在する理由は、学習を支援し、理想的にはコードを改善するためです。

コピーして貼り付けますか?誰も気にしない!重要なのは、コピーして貼り付ける理由です。私はここの誰に対しても哲学的になろうとはしていませんが、これについて実際に考えてみましょう:

怠ではないのですか? "ブラー、私は前にこれをやったことがあります...私はいくつかの変数名を変更するだけです.. done。"

コピーして貼り付ける前に既に適切なコードだった場合は問題ありません。そうでなければ、あなたは怠ofからくだらないコードを永続させてしまい、あなたのお尻を噛み砕いてしまいます。

あなたが理解していないからですか? "くそー。その機能がどのように機能するのか理解していないが、自分のコードで機能するかどうかは疑問だ。"かもしれない!これにより、午前9時に期限があり、午前4時頃の時計を赤目で見ていると強調された瞬間に時間を節約できます。

このコードに戻るとき、このコードを理解しますか?コメントしても?本当に-数千行のコードの後で、コードを書いているときにコードが何をしているのか理解していない場合、数週間後、数ヶ月後にどのように戻ってくるかを理解しますか?それ以外のすべての誘惑にもかかわらず、それを学ぶことを試みます。それを入力します、これはメモリにコミットするのに役立ちます。入力する各行は、その行が何をしていて、その機能の全体的な目的にどのように貢献しているかを自問してください。裏で学習しなくても、後で戻ったときに少なくとも認識できる可能性があります。

だから-コードをコピーして貼り付けますか?あなたがしていることの意味を意識しているのなら大丈夫です。さもないと?しないでください。また、コピーして貼り付けるサードパーティコードのライセンスのコピーがあることを確認してください。常識のように思えますが、そうでない人が多いことに驚かれることでしょう。

ペストのような切り貼りは避けます。それは、そのいとこクローンと変更よりもさらに悪いです。あなたのような状況に直面した場合、マクロプロセッサまたは他のスクリプトを使用してさまざまなバリエーションを生成する準備ができています。私の経験では、単一の真実のポイントは非常に重要です。

残念ながら、Cマクロプロセッサは、改行、式、ステートメント、および引数のクォート要件が面倒なので、この目的にはあまり適していません。書くのが嫌い

#define RETPOS(E) do { if ((E) > 0) then return; } while(0)

しかし、その引用は必要です。ツールチェーンに別の項目を追加しないため、ビルドプロセスやMakefileを変更する必要がないため、Cプリプロセッサを使用することがよくあります。

これは主観的なものとしてタグ付けされているのはうれしいです。これは非常に曖昧な例ですが、重複するコードが十分にある場合、それらのセクションを抽象化し、異なる部分を異なるものに保つことができると思います。コピー&ペーストしないことのポイントは、維持するのが難しくて壊れやすいコードを持たないようにすることです。

(一般的な関数への変換またはマクロの使用以外に)最良の方法は、コメントを挿入することです。コードのコピー元とコピー先、コメントの共通点、相違点、およびその理由...それで大丈夫です。

ほとんど同じ機能を持っているが、異なるシナリオではわずかな調整が必要であることがわかった場合、問題は設計にあります。フラグまたはコピーペーストの代わりに多態性と合成を使用します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top