DRYが重要な理由


81

非常に簡単ですが、同じプロセスをいくつかの小さな調整で数回繰り返すだけで、すべてのケースとスケーラブルなデータで機能するコードを作成したいのはなぜですか?

すぐにこれを再度編集する必要はないでしょう。

行くだけで仕事が減るみたいです...

function doStuff1(){/*.a.*/}
function doStuff2(){/*.b.*/}
function doStuff3(){/*.c.*/}

そして、何かを追加する必要がある場合...

function doStuff4(){/*.d.*/}

削除する必要がある場合は、削除します。

すべてをすべてのケースにデータを供給して処理できる1つの単純なパターンにすべてを作り、私がこれまでにないような気がしない多くの変更を行う方法を理解することは困難ですする。

クイックカット+ペーストで作業が大幅に減るのに、どうしてDRYになるのですか?


11
なぜなら、あなたが正しいことをすれば、乾燥はまだ速いのです。他のすべてに影響を与える
ダニエルリトル

97
「すぐにこれを再び編集する必要はありそうにありません」 -あなたは望むかもしれませんが、おそらくここで間違いを犯しているでしょう。そして、そのコードで再び作業するつもりで、すぐにそうしないと、事態が悪化するだけです。重複がどこにあるかを忘れてしまい、重複は微妙ではあるが危険な矛盾になります。「あなたのコードを保守する人が、あなたが住んでいる場所を知っている危険なマニアであるかのように書いてください」、古典を引用してください。
9000

14
私はあなたがそれをかなり要約することができると思います:単一の変化点は維持するのがより簡単です。
ファルコン

17
これに自分で答えることができない場合は、開発と保守の実世界での経験をさらに得る必要があります。
デビッドヘファーナン

15
@Wayne何百万人ものプログラマーが突然大声で叫んだように、ソースに大きな混乱を感じました。
シークレット

回答:


121

繰り返すと、保守性の問題が生じる可能性があります。doStuff1-3のすべてに同様の構造のコードがあり、1つの問題を修正した場合、他の場所で問題を修正するのを簡単に忘れることがあります。また、処理する新しいケースを追加する必要がある場合は、場所全体にコピーアンドペーストするのではなく、異なるパラメーターを1つの関数に渡すだけで済みます。

ただし、DRYはしばしば、賢いプログラマーによって極端に扱われます。自分自身を繰り返さないようにするには、抽象化を作成して、チームメイトがそれに追随できないように鈍感にする必要がある場合があります。2つのものの構造があいまいに似ているだけで、十分に異なる場合があります。doStuff1-4が十分に異なるため、リファクタリングを繰り返して自分自身を繰り返さない場合、不自然なコードを記述したり、巧妙なコーディングバックフリップを行ってチームを眩惑させたりする必要があります。私は不自然な方法で数回自分自身を繰り返さないように後ろ向きに曲がり、最終製品を後悔しました。

私はいつもDRYの側で間違いを犯します。まれに、読みやすさのメリットは、複数の場所でバグを修正するのを忘れるリスクに見合う価値があると思います。

そのアドバイスを考慮すると、あなたの場合のように聞こえます

いくつかのマイナーな調整で同じプロセスを数回繰り返します

私はあなたのケースで自分自身を繰り返さないように間違いなく努力します。最小限の「微調整」を想定-振る舞いに影響を与えるさまざまなパラメーターで処理したり、依存関係を注入してさまざまなサブタスクを実行したりできます。

クイックカット+ペーストで作業が大幅に減るのに、どうしてDRYになるのですか?

有名な最後の言葉。ジュニアエンジニアが1つのdoStuffを微調整/修正/リファクタリングし、他のDoStuffが存在することすら認識していないと考えることを後悔します。陽気が続きます。ほとんど胸焼けは起こりません。コードのすべての行にはさらに費用がかかります。非常に多くの繰り返し関数を使用して、いくつのコードパスをテストする必要がありますか?1つの機能であれば、いくつかの動作を変更して1つのメインパスをテストするだけです。コピー&ペーストする場合、すべてのdoStuffを個別にテストする必要があります。紛失すると、顧客に好ましくないバグが発生し、受信トレイに好ましくないメールが届く可能性があります。


2
えー、明確にするために、私は乾燥が悪いと提案しているわけではありません。この質問は悪魔が提唱するものです。カット+ペースト+微調整のコードは問題ないと思う人にリンクできる論理的な応答を本当に探しています。
シークレット

2
そうは言っても、DRYの両方の失敗をカバーしているので、私はあなたの答えが最も好きです。-バグの読みやすさを犠牲にすることについての1つの点で、繰り返しコードは、指摘したのと同じ理由で読みにくくなると主張します。
シークレット

16
DRYの1つの簡単な落とし穴を強調します。同様のコードをマージします。機能的に関係のない2つのユースケースがあり、コードが非常に似ている場合、DRYが優れているため、2つを簡単にマージできます。残念ながら、進化する必要がある場合、関数をもう一度分割し、すべての呼び出しサイトを調べて、ここでどちらを呼び出すかを慎重に検討するという不愉快なタスクに直面することがよくあります...例:LLVM構造型(同様のタイプはすべて1つにマージされます)、IRを元のコードにマップすることはほぼ不可能になります。
マチューM.

1
ビンゴ。コードの2つ以上の部分が変更され、変更がすべての部分で常に同じ場合は、それらをマージする必要があります。他とは異なる方法で変更する必要がある部分は、マージしないでください。コードがまったく変更されない場合、コードがマージされているかどうかは重要ではありません。変更をロックするか切り離すかという問題は、問題のコードのサイズよりもはるかに重要です。
supercat

1
@MatthieuMそれは少し不公平な例です。LLVMは、最適化として構造タイピングを適用しています。つまり、LLVMの人々は、パフォーマンスの利点のために、理解しにくいIRの代価を支払うことにしました。DRYは通常、保守性の問題ですが、この場合、保守性を低下させることは明らかに意図的な決定でした。
ベンジャミンホジソン

47

DRYのほうが後で作業が少なくなるためです。


DRY:(自分自身を繰り返さないでください)

引数を取る1つの関数。

def log(arg):
    print(arg)

C&P:(コピー&ペースト)

26兆個の関数は基本的に同じことを行いますが、2文字の違いがあります。

def logA():
    print('a')

def logB():
    print('b')

...ad infinitum...

印刷を更新して、印刷内容を正確に指定する方法はありますか?

ドライ:

def log(arg):
    print(arg + "Printed from process foo")

できた

C&P:

戻ってすべての機能を変更する必要があります


どちらをデバッグする方が簡単だと思いますか?


10
また、重複する関数がある限り、同様のテストスイートを作成する必要があります。
9000

概念を適切に説明しましたが、実際には、ガシリオン関数を使用して説明したことを実行する人はいません。
ロバートハーヴェイ

@ロバート私は望みません!コンセプトと、なぜそれが良いことなのかをより良く説明するために、非常に簡単なタスクを選びました。
ジョン

11
@Robert -あなたはでの記事のいずれかを読んでいるthedailywtf.com ;) -一部はちょうどそれを行うだろう誰がそこにある
HorusKol

1
@ 9000テストスイートがない場合:p(実際には多くの場合、一部のプロジェクトに当てはまる可能性があります...残念ながら...)
ヴィッシュ

16

なぜなら、あなたの例に適用された:

  • +読みやすさ

    コード少ないほど、ノイズ少なくなります(常にではない...)

  • +柔軟性

    の動作を変更する必要があった場合はdoStuffX、自分自身またはそれを書いた人を殺したいでしょう。

  • +拡張性

    選択したデータ構造に個別の部分を抽出し、genericを呼び出してそれを繰り返した場合doStuff、データ構造に新しいエントリが必要な場所に1行追加するか、削除することができます。動作を変更することは、単に編集することを意味しますdoStuffメンテナンスが簡単。

  • +コスト効率

    ここのコードが少ないということは:

    • => 開発の減少 => コストの削減
    • => バグの可能性が少ない => サポート時間が短い => コストを削減
  • +(可能な)管理された最適化

    言語によっては、コンパイラ/インタープリターは、ジェネリックdoStuffが常にほぼ同じことを頻繁に次々と呼び出し、インライン化または最適化を試みる可能性があると判断する可能性が高くなります。のXバリエーションにはおそらくないでしょうdoStuffX

  • +テストと品質

    テストは簡単です。テストがdoStuff必要です。それだけです。まあ、正確ではありませんが、それはすでに多くをカバーしています。唯一のそのIOの期待は異なり、異なる条件下で試験する必要があるが、それはまだだ多くのテストが容易より保守のすべてのバリエーションよりdoStuffX

全体として、これはチームのメンテナンス性の高いコードと開発効率の向上を説明するものであり、より堅牢で信頼性の高いソフトウェアの作成を支援する多くの優れたプラクティスの1つです。


13

誰もが重複コードの保守性の問題を説明するのに素晴らしい仕事をしたので、私はこれを言うだけです:

プログラミングの多くは、当面の現在だけでなく、未来について考える必要があります。コピー&ペーストが簡単になったというのは正しいことですが、「すぐにまた編集する必要はない」という声明は、あなたが正しく考えていないことを示しています。はい、あなたは迅速で汚いコピー/貼り付けですが、そうすることで、当面の問題を超えて明日を考えることができないことを示しています。このコードを再訪する必要は決してないと確信していますか?バグはありませんか?次の機能セットを実装する必要があるときに、再訪する必要がないことを100%保証できますか?これらは明日の問題であり、今日の設計時に考慮する必要があります。

もちろん、コピー/貼り付けが必要になる場合があります。UI開発者として、DRYの原則に違反しなければならないことがあることがわかりました。それはひどく、私はそれが起こるたびに縮んで、ありがたいことに、それはまれです。しかし、それ起こります。

違いは、DRYに違反した場合、そうする非常に説得力のある理由が必要であるということです。また、これらのすべてを1つの単純なパターンにする方法を理解するのは難しいのです。あなたが大規模な時間の危機にさらされており、上司が数時間以内に何かを得るために叫んでいない限り、またはあなたが仕事を失うことになるまで、私はこれが有効な理由だとは思わない。

これを間違った方法で受け取らないでください。私はあなたを非難したり、懲らしめたりしようとはしていません。プログラマーは将来の怠lazに投資します。DRYはそれを達成する方法です。困難な設計問題を解決するために今日あなたがやる仕事は明日報われるでしょう。


私は、上司が「それを成し遂げるか解雇された」とDRYに違反する大きな理由だと同意するかどうかはわかりません。技術的な負債は、いつそれを正しく行う時間があるのか​​、本当に時間の節約などですか?
シークレット

1
@Incognito、私は少し皮肉なことをしようとしていた:)
bedwyr

7

すぐにこれを再度編集する必要はないでしょう。

これが本当に当てはまる場合、それを回避できる可能性がありますが、多くの場合、維持する必要があるコードで作業することになります。つまり、機能の拡張、バグの修正、その他の改善を意味します。10か所に同じコードの小さなバリエーションがあり、いつかそのコードに戻って変更を加える必要がある場合、10か所に同じ変更を加えるというエラーが発生しやすいタスクがあります(申し訳ありませんが、 11か所ありましたが、1か所を忘れてしまい、バグが発生しました)。

解決しようとしている問題を一般化できれば、バグが発生した場合にコードを簡単に拡張および修正できます。


クールな答えですが、私がそれをうまくやっても、私の後にそれを維持するようになる貧しい樹液はどうですか?;)。
シークレット

参照:「メンテナンスが必要なコードの作業を頻繁に行う」:)
Alex

その場合でも、コピーして貼り付けるのが100%バグのない完璧な場合だけです。そして、そうではなく、そうかもしれないと考えるのをやめてください。
ダン・レイ

過去のものをコピーして貼り付けたことは認めますが、何もするために1日以上持ち歩くつもりはありませんでした。場合によっては、迅速で汚いスローアウェイスクリプトが必要なだけです。
アレックス

6

別の質問の答えで述べたように、私のアプローチは次のとおりです。

  1. 特定の問題を初めて解決するとき、私はそれをやり遂げます。
  2. 2回目(つまり、同様の問題を解決するとき)私は思う:hm、多分私は自分自身を繰り返しているが、今のところ簡単なコピーアンドペーストに行きます。
  3. 3回目に思う:フム、私は自分自身を繰り返す->それを一般的にする!

つまり、2つまでの別の原則(YAGNI)がDRYに勝ちます。しかし、3(または私が本当に怠けている場合は4)から始めて、私はそれを必要とするようであるので、DRYに従います。

更新

私の最近の経験からのいくつかのさらなるアイデア。別のチームが開発した2つのコンポーネントAとBを製品に適合/統合する必要がありました。まず、2つのコンポーネントAとB Bは互いに非常に類似しているため、アーキテクチャが多少異なるという事実に既に悩まされていました。2番目:サブクラスを使用して、本当に必要なものだけをオーバーライドして良かったので、それらを適応させる必要がありました。

そこで、これら2つのコンポーネント(それぞれ約8つのC ++クラスで構成されています)のリファクタリングを開始しました。AとBの両方に共通のアーキテクチャを用意し、サブクラスを定義して必要な機能を追加しました。このようにして、2つの新しいコンポーネントA 'とB'は既存のコンポーネントから派生します。

既存のコードから一般的で明確な構造を取得しようとして2週間後、毎日の会議で元のコードが面倒であまり進歩していないことを説明しなければならなかったので、上司に話しました。これらの2つの新しいコンポーネントA 'およびB'以外は必要ないことがわかりました(4つまたは6つではなく、2つだけでした)。

わかりましたので、AとBからクラスの大規模なコピーと名前変更を行い、コードのコピーの適応を開始しました。あと2週間で動作するようになりました(今でもバグ修正を行っています)。

利点:機能がほぼ完成し、すべてのバグを修正したら完成しました。AとBのすべてのリファクタリングとテストを保存しました。

欠点:2週間前、他のチームはAとBが使用する別のコンポーネントCを変更しました。AとBを適合させましたが、A 'とB'も壊れていたため、自分で変更する必要がありました。これにより、修正が必要な新しいバグが発生しました。A 'とB'がコードの大部分をAとBと共有している場合、この余分な作業はおそらく不要だったでしょう。

そのため、コードの複製は常に危険です。トレードオフを見つけることは常に問題であり、多くの場合それは簡単ではないと思います。


5

明確にするために、他の回答のいずれにもこれが見つからないので:

DRYは、あらゆる種類の情報の繰り返しを減らすことを目的としたソフトウェア開発の原則です。

すべての知識は、システム内で単一の明確な権威ある表現を持たなければなりません。

Andy HuntとDave Thomasが言及したDRYの原則は、コードの重複を防ぐことに限定されません。また、コード生成と自動化プロセスを提唱しています。皮肉なことに、コード生成の結果は重複コードになる可能性さえあります...

理由はすでに他の回答で徹底的に説明されていますが、ファルコンのコメントは私見で十分に要約しています:

単一の変更点を維持する方が簡単です。


ああ、タグにデータがあると思った。そこに情報を入れます。
シークレット

3

乾燥しすぎているようなことがあります。これが発生すると、ある時点でファクタリングコード(1)を保証するのに十分に似ていると思われる2つの概念が、後で別々の実装に値するほど異なることが判明する場合があります。

つまり、DRYと疎結合が競合する場合があります。doStuff1と友人がソフトウェアの新しいリリースごとに分岐することが予想される場合、コードを複製しても構いません。

私の経験では、あなたのソフトウェアが将来どこへ行くのかを判断するのは難しいかもしれません。そのため、DRYはしばしば安全な選択です。

過度に「ドライ」されたコードは、通常、複雑な制御フローと多すぎるパラメーターを持っています。当初は単純な関数でしたが、その後、追加のパラメーターによって制御される新しい機能をサポートするために拡張されました。2〜3回繰り返した後、関数はメンテナンスできなくなります。設定で発生するバグを修正し、他の設定で新しいバグを導入します。

コードが進化するにつれてコードの品質が低下することがよくあることは理解できますが、本文にif-then-elseスパゲッティを使用したマルチパラメーター関数が、意味のあるリファクタリング努力の結果である場合を見てきました。

(1)「コード」という言葉を使用していますが、これはデザインにも当てはまります。


「あまりにも乾燥しすぎている」という例を挙げると、スペクトルの終わりがあまり見えないので役立ちます。
シークレット

@Incognito:回答を編集しました。具体的な例はありませんが、うまくいけば私が意図したことは十分に明確です。
Joh

2

リレーショナルデータベースの世界におけるDRYの問題に言及する必要があります。データベースは、セットベースのロジックを使用して、検索可能なクエリを通じて迅速かつ適切に実行するように設計されています。DRYの原則により、多くの場合、開発者は、Sargableでないクエリを記述したり、Row-by-agonizing-Rowロジックを使用して、複数の状況で既存のコードを活用したりします。DRYとパフォーマンスの最適化は相反する場合が多く、データベースの世界では、パフォーマンスは通常、保守性よりもはるかに重要です。これは、DRYの原則をまったく使用しないことを意味するものではなく、データベースの全体的なユーザビリティにどのように影響するかを知っておく必要があるだけです。アプリケーション開発者は最初にDRY、パフォーマンス2番目、データベース開発者は最初にデータの整合性、パフォーマンス2番目、データのセキュリティ3番目(パフォーマンスとセキュリティはシステムによっては入れ替わることがあります)と考えます。

一般的に、データベースクエリに追加する抽象化の層が多くなるほど遅くなることに気付きました。データベースのパフォーマンスに影響を与えずに開発者がDRYを使用できるように、datbaseプログラムを設計する人々自身がより良い仕事をしたくないとは言っていませんが、そのレベルのデータベースソフトウェアは設計していません、したがって、データベースの抽象化とパフォーマンスの競合は、おそらく私が思うよりも修正するのが難しいです。ただし、現在構築されているシステムを使用する必要があります。パフォーマンスを損なうことのない将来のリリースでのDRY原則のより良い実装を求めることができます(そして、それは長年にわたって改善されましたが、それでも問題があります)。現時点では。

しかし、多くの場合、DRYの原則を確実に満たすために使用する機能は、データベースに大きな問題を引き起こすものです。私は決してDRYを使用しないと言っているわけではありませんが、DRYを使いすぎないでください。

私が話していることの例。月に1回、100万件のレコードのデータインポートを行う必要があります。レコードは、ストアドプロシージャを呼び出すユーザーインターフェイスを介して手動で追加できます。このプロシージャは、単一レコードのインポート用に設計されているため、一度に1レコードのみ追加します。DRYを使用して、挿入コードが2か所にあるのを避けるために、必要なセットベースのインポートを記述するのではなく、カーソルを記述してprocを繰り返し呼び出します。インポートの時間は、セットベースのロジックを使用した場合の30分から18時間になります。この場合、DRYに準拠する正しい方法は、複数レコードのインポートを処理するようにprocを修正することです。残念ながら、配列をprocに送信することは不可能であるか非常に困難であり(dbバックエンドによって異なります)、procを変更することにより、アプリケーションが壊れてしまいます。

スカラー関数とテーブル値関数もDRY原則の実装に使用されますが、特にインデックスの有用性を妨げる方法でそれらを使用する必要がある場合、パフォーマンスに深刻な影響を与える可能性があります。

ビューはDRYの実装にも適しています。ただし、他のビューを呼び出すビューを呼び出すビューを使用してDRYを実装すると、負荷がかかるとクエリがタイムアウトするポイントにすぐに到達します。実際、最後に必要なレコードが3つだけの場合、数百万のレコードのデータセットを生成する必要が生じる場合があります。したがって、DRYを実装するための複雑な結合セットの1レベルビューは優れている場合があります(すべての財務レポートが特定のテーブルの特定の基本セットと計算を使用することを確認するために使用する1つがあります)。パフォーマンスの混乱を引き起こしているかどうかを考慮する必要があります。


1

上記の私の答えの要点が見当たらないので、ここに行きます。DRYを原則として見ないでくださいに対するなんかやってる。それはそのように言い表されるかもしれませんが、それは実際に全く異なった前向きな目的に役立つことができます。それは停止し、考え、より良い答えを見つけるための合図です。より良いソリューションを設計する機会を探すことは私に挑戦します。これは、コードの悪臭の良い面であり、デザインを再考するように誘導し、より良いものにします。DRYは、単なるちっぽけな構文違反ではありません。モジュール化することは私に挑戦します。コンポーネント化することは私に挑戦します。それは、ブルートフォースと無知の代わりにテンプレートとコード生成の使用について考えるように思い出させる繰り返しを示しています。自動化を自動化する時間を見つける必要があることを理解するのに役立ちます。それはあなたをpar約的なライフスタイルへと導きます!ちょっとしたつまらない退屈な詳細ではなく、もっとクールな新しいことをするのにより多くの時間を費やすことができます。そして、それはあなたに良いマナー、良い息、そして健康的なライフスタイルを与えます!まあ、おそらく私は少し迷っています....


DRYは私に非常に異なる効果をもたらしますが、もしこれらがあなたに対する効果であるなら、私は何かを「止め、考え、より良い答えを見つけるための合図」であるという哲学と挑戦的です。
n611x007

1

私は古いレガシープロジェクトを持っていて、元開発者の何人かはDRYをまったく気にしませんでした。そのため、コードベース全体は、GetSystemTimeAsString()、LogToFile()などのヘルパーメソッドでいっぱいになりました。一部のメソッドは特別なニーズに合わせてわずかにカスタマイズされましたが、ほとんどは単なるコピーアンドペーストでした。

残念ながら、一部のメソッドには、strcpy()などの安全でないものを使用するchar配列などの微妙なバグがありました。

そのため、すべてのコードフラグメントを見つけ、それらを調和させ、バグを修正するのは本当のPITAでした。そして、私たちはまだ物の調和と修正を行っています。

最初の方法で間違いを犯し、それをコピーしただけなので、何度も修正する必要がある場合、あなたは決して知りません。そして、後でいくつかのメソッドを使用したい場合、コードベースの5つのメソッドのどれがあなたのケースに合っているかをどのように知っていますか?コピーしてカスタマイズするだけで、ここから再開できます...



1

「自分自身を繰り返さない」という言い回しは少し単純化しすぎています。重要なのは、「2つの独立した場所にカプセル化された1つの潜在的に変更可能な情報を持たないこと」です。

プログラムがウィジェットを処理することになっている場合、各ウィジェットは3つのwoozleを持ち、フォームの多くのループです

for (i=0; i<3; i++)
  thisWidget.processWoozle(i);

ウィジェットに3つのウズルが含まれると予想されることは、これらのループのそれぞれにカプセル化され、ウィジェットごとに他の数のウズルに対応するようにコードを更新することは困難です。対照的に、もし誰かが言うなら

#define WOOZLES_PER_WIDGET 3

そして、各ループが書き直されました

for (i=0; i<WOOZLES_PER_WIDGET; i++) ...

そのようなデザインにより、ウィジェットごとのウーゼルの数を非常に簡単に変更できるます。

ただし、ウィジェットあたりのウーゼルの数などの情報を1つのポイントに統合することが望ましいが、常に実用的ではないことに注意することが重要です。物事が特定のサイズである場合にのみ機能するロジックをハードコードする必要がある場合があります。たとえば、各woozleに値があり、特定のウィジェットに関連付けられた中央値を検索する場合、値を並べ替えて中央の値を取ることができます。このようなアプローチは、任意の数のwoozlesで機能しますが、ロジックこれは、3つのアイテムの中央値を見つけるために特に手書きで書かれています。

WOOZLES_PER_WIDGET定数があるとコードが読みやすくなりますが、プログラムロジックに他の調整を行わないとその値を変更できないことを明確にするためにコメントする必要があります。その場合、3つのアイテムと定数WOOZLES_PER_WIDGETにハードコーディングされたロジックは両方とも「各ウィジェットには3つのウーゼルがある」という情報を複製しますが、そのような複製の利点(実行速度の向上)はコストを上回ります。


0

私は実際に保守性などに関する他のポスターのコメントに同意していますが、それらはすべて有効です。

私は議論に小さな反対意見を加えたいと思います。

  • プログラマーにとって唯一重要です。あなたの賃金を支払う人々は、ソフトウェアがUATに合格する限り、それほど気にすることはできません。
  • 重要性の点では、適切な要件の取得、プロジェクトスポンサーの意見の聞き取り、時間通りの納品などの項目よりもかなり下にランクされています。

このサイトは「プログラマー」向けなので、質問は「プログラマーの視点」に向けられていると言っても安全だと思います。賃金支払者、UAT、および重要度のランクに関するあなたの声明はもちろん有効ですが、この特定の質問には関係ありません。
ozz

1
私はまったく同意しません。優れた経営者は、原則と、それが詳細に説明されていれば、その理由を理解します。これは深刻な詳細な会話であり、5分間のドロップではありません。
マイケルデュラント

2
2番目の箇条書きは完全に正しいです。
ジムG.

1
@Ozz、あなたの技術に対する誇りは、優れたプログラマーにとっても重要です。しかし、おそらく「プログラマーの視点」には、「顧客満足」に対する若干の懸念が含まれるべきです。
ジェームスアンダーソン

0

<tl;dr>

繰り返されたすべての答えを読むことができなかったので、何かを見逃したかもしれません(そして、自分で繰り返します<=ここで私がしたことを見る?)。

ここに、コードの重複を防ぐために素晴らしいもののリストがあります!

  1. テストが簡単:コードの「コピー」を1つだけテストする必要があります。
  2. 修正が簡単:コードの1つの「コピー」でバグを見つけて、一度修正するだけで済みます。
  3. 更新が簡単(上記と同じ):必要な変更は、コードを適切に再利用するのに時間がかかり、同じ行をソース内の数百または数千の異なる場所にコピーしなかったため、非常に少ない場所でコードを変更することで処理できることがよくあります
  4. 再利用がより簡単:いくつかの場所で複製されず、適切な名前が付けられた汎用メソッドに保持されている場合、独自のメソッドを作成する代わりにそれらを見つけて使用するのは簡単です。
  5. 読みやすい:重複したコードは不必要に冗長であるため、読みにくいです。ロジックと特定の機能の一部ではない多くの行が含まれています(たとえば、アクションを実行するためのステージをセットアップするために使用される一般的なコマンドや、多くの場所で必要な一般的な単純な繰り返しタスクなど)。クリーンなコードを使用すると、コードスペースが散らかって繰り返されることがないため、ロジックと機能がポップアウトされます。
  6. (1)と(5)により、デバッグが容易になります。
  7. 時間とお金を節約し、将来的にもっと楽しいことをします。具体的には、より優れた堅牢なコードを作成します。これが一番下の行であり、上記のほとんどすべての合計です。多くの人が同じ機能を使用している場合、doFoo1(a, b)その迷惑な障害やエッジケースの多くが発見され解決される可能性が高くなります。誰もがコードをコピーして作成した場合doFoo2(specialA)... doFuu2^n(a, b, c)彼らはで問題を重複doFoo1し、具体的に、より多くの仕事を作成しました。

</tl;dr>

ロングバージョン:

コード重複の問題は、「指数関数的に成長する」(つまり、急速に拡張する)ことです。コードを複製すると、知らないうちに他の人に許可を与えます(たとえば、あなたはもはやそれらを判断する立場にありません)同じことをすることをお勧めします。また、ソースに混乱を招く冗長な繰り返しが多数ある場合、有用なコードを見つけて再利用するのが難しくなるため、困難になりません。特に、コードが適切な名前の関数にまだ抽出されていない場合。したがって、一般的な単純な問題を解決する場合、それを解決するコードを自分で作成する可能性が高くなります。また、バグのある未テストコードを追加して、いくつかのエッジケースのチェックに失敗する可能性があります。

もう1つのことは、初心者にとっては大企業にしか影響を与えない問題のように聞こえるかもしれませんが、小さなスタートアップをひどく苦しめることがわかりました(10,000行のサーバー側コードの重複など)。それは心の状態です。DRYをマスターするだけでなく、他の人にも同じことをするように励ましてください。そうしないと、コードのほとんどを複製することになります。DRYの手段が手元にあり、施行されている場合、それらを適用するのがはるかに簡単です。重複するコードが多数ある場合、コピーペーストソリューションを適用する方がはるかに簡単です。

コードの複製で有害と感じるもの:

  1. この機能は使えますか?機能する(または必要なことを行うように見える)機能を見つけたとしましょう。正しく機能するはずなのか、それとも複製されて破棄されたコードだけなのかをどのように知るのでしょうか。
  2. 冗長コード。時々、人々はコードを複製し、それを使用し、それを忘れます(彼らは将来、いつでも再び複製することができます)。ある時点で、誰かがリファクタリングを試みて一部の場所で重複した関数への呼び出しを削除しますが、アクティブに使用されていなくても未使用の関数は残ります。
  3. あなたが探しているものを見つけるのは難しい。複製されたコードはスペースを占有し、(grepのようなツールを使用して)有用で必要なものを見つけることを、ほんの一握りしか得られなかった数十または数千の結果を得るのに比べて困難な作業にします。
  4. (以前に言及しました):保守が難しいだけでなく、保守および回帰の目的で使用するのも困難です。テストコードが複製され、関数に適切に抽出されない場合、他の人がそれを複製します。QoLを改善するために、使いやすく読みやすいAPIを書くことに悩む人はいますか?私の経験では、いや、手に負えなくなるまで、人々はもっと差し迫った問題だと考えるものがしばしばあります。
  5. コードの重複は、冗長性が意図した機能に関する情報を追加しない場所で、コードを必要のない場所で冗長にするため、読みにくくなります。たとえば、使用される[繰り返し]汎用メソッド呼び出し複数の種類の意図された機能の基盤を設定し、この実際の機能が飛び出すのを難しくします。
  6. これは多く言及されています。コードが間違っている場合、一部の貧しいギャルや男は、そのコードの各使用を検索して変更する必要があります。たとえば、誰かが必要な組織化されたクラスのごく少数の場所でmysql_queryにSQLインジェクションの安全でない呼び出しを使用した場合、それを修正し、代わりにPHP PDOを使用するのは簡単ですが、呼び出しをコピーする1000以上の場所で使用した場合そして、それを修正することは実際には外部委託する必要があり、時にはより危険な場合には、コードをゼロから書き直す必要があります。
  7. コードの複製は悪い習慣です。あなたが何かを練習すると、それはゆっくりと第二の性質になり、あなたの周りの人々に影響を与えます。ジュニア開発者は、あなたがそれをしているのを見て、それもしている。あなたが説教することを練習し、正しいことをする習慣を作るべきです。あなたはもっと学びます。複製されていないコードを書くことは難しく、より困難です。それはやりがいのある習慣です。

熱心なコード重複の防止とまとめ:

これは以前にも言われましたが、重複を避けると「逆向きに曲がる」ことがあり、他の人には理解できないほど洗練された(または重要ではない)ことをします。読み取り不可能なコードの記述(または冗談で "ジョブ保存"コードと呼ぶ)は、コードの重複を防ぐことが関係しない場合でも、それ自体が問題です。ただし、最初から適切なインフラストラクチャとベストプラクティスを導入すると、コードの重複を防ぐのがはるかに簡単になり、直感的でないことを避けることができるため、コードの重複防止のために行われる将来の不要で読みにくい作業の積み重ねを防ぐことができますあなたはその最初から正しいことをします。

正しいことは何ですか?それは答えるのが難しい質問ですが、1つのことは、プロジェクトに必要なメソッドを定義し、社内(社外)で既に実装されているものを確認し、可能な場合はそれを再利用することです。コードベースに追加するすべてを文書化し、必要以上に一般的なものにすることを試みますが、それだけです。必要のないコードを柔軟にするためだけに、デザインパターンを無理にしないでください。

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