重複コードを受け入れることができる例外的なケースはありますか?


57

私は、3つのAPIを構築する必要があるソフトウェアプロジェクトに取り組んでいます。1つはホームバンキングチャネル用、もう1つは代理店チャネル用、3つ目はモバイルチャネル用です。

エージェンシーAPIは、すべての機能を備えているため、最も完成度の高いAPIです。

ここでのアーキテクトは、共通層(すべてのAPIで共有されるクロスチャネルEJBサービス)を作成しました。ただし、APIは異なります。

現在のところ、API間に大きな違いはありません。大きなチームはエージェンシーチャンネルから始まり、現在はホームチャンネルに適応しています。私たちは、単にホームアプリ専用のオブジェクトを強化しています。それ以外の場合、コードはAPI間で95%類似しています。APIはSpring MVCの上に構築されており、(コントローラー、モデル、およびいくつかのユーティリティ)があります。

基本的に、コントローラーはBOをChannelObject(これを行うのに適切な場所ではないようです)、およびいくつかの追加のユーティリティとシリアライザーへのマッピングBOを実行しています。今のところすべてが重複しています。重複の理由は、APIを独立させたいからだと言っています。「明日、私たちが代理店や携帯電話とは異なる行動を家庭で望むなら、私たちは苦労しません!」

重複コードを受け入れる必要がある場合はありますか?


22
そして、明日3人が明日、すべてのAPI間で一貫したデータアクセスと表現が必要だと判断した場合...まあ...「闘争!」
Becuzz

26
コードの重複は必ずしも悪いことではありません。「DRYはデカップリングの敵です」という言葉を十分に強調することはできません。今のところではなく、未来のために設計することは本当に非常に悪いことです。その未来はほとんど実現しません。代わりに、現在必要なものに対する優れた自動テストでカバーされた、高度に分離されたソリューションを設計します。その後、将来、別の何かが必要になった場合、変更しやすくなります。
デビッドアルノ

2
コードの複製を後悔したことがない2つのケースは、(a)複製されたコードが非常に小さく全体の重要な部分ではない場合と、(b)すでに死にかけているシステムからコードをコピーしている場合だと思います長寿のために設計された新しいシステムに。コードをフォークした後に苦い涙を流した他の多くのケースがあります。
マイケルケイ

14
私が働いたところでは、1度(しばしば)複製し、3度目に共通性を抽象化する必要があることがしばしば指摘されてきました。これは、凝集の代わりに結合を増加させる、初期の、場合によっては不適切な抽象化の時代精神を取り除きます。将来の要件が実際に十分に理解されている場合、例外を作成できます。
ピーターギアケンズ

3
重複コードが許容される可能性のある些細なケースは、自動生成された場合です
-samgak

回答:


70

複製が可能ではなく、このような理由のために、行うには正しいものになります。

「コードベースのこれらの3つの場所は、現在は同じですが、同じように振る舞わせたい」と言うことは、大規模な複製の正当な理由ではありません。この概念はすべてのシステムに適用でき重複を正当化するために使用できますが、これは明らかに合理的ではありません。

重複は、それを削除することが他の何らかの理由で現在全体的により高価になる場合にのみ許容されるべきです(今のところ良いものを考えることはできませんが、1つが存在する可能性があります-プログラミングの事実上すべてはトレードオフではなくトレードオフです法律)。

あなたがしていることについて、正しい解決策は、たとえば、現在複製されている動作を戦略またはクラスとして動作をモデル化する他のパターンに抽出し、同じクラスの3つのインスタンスを使用することです。そうすれば、あなたはとき 3ヶ所のいずれかで動作を変更したい、あなただけの新しい戦略を作成して、一つの場所にそれをインスタンス化する必要があります。そうすれば、いくつかのクラスを追加するだけで、残りのコードベースはほとんど完全にそのままです。


1
2つのことが同じように動作する場合、それは重複です。ただし、何らかの理由で同じように動作しない限り、マージすることはできません。実装の一般的な部分を抽出できますか?時々、まだ抽象化されていないビルディングブロックがあります。

32
例として、アプリケーションの3つの部分で使用されるコードのチャンクがあり、それらの3つのそれぞれの例外をカバーするために、至る所に小さな条件分岐がたくさんあり、かなりひどく書かれていました。しかし、これは重要なビットであり、2年もの間、重大なバグ報告は一切なく、頻繁に使用されていました。そのため、アプリケーションの4番目の部分で何かを行う必要があり、わずかに異なる場合、完全に実行された欠陥のあるコードには触れず、それをコピーして、より適切に記述された柔軟なベースを作成するという決定が下されました未来。
KRyan

2
重複は、最終的にまとめられるメンテナンスコストと比較して、読みやすさのコストが上がりすぎる場合に行うべき正しいことです。これはこのシナリオではまったく当てはまらないと思いますが、多くの場合、このようなものではなく、プロジェクトの小規模で見られます。その場合でも、これが当てはまる状況になることは非常にまれです。小さな方法でテンプレートメソッドパターンなどを使用することを余儀なくされるニッチネストされた複製がある場合に発生します。これは通常、コードを難読化することになります。
opa

複製が有用な例(および「削除すると全体的にコストがかかるようになりました」)は、Webアプリケーションでの入力検証です。クライアントは信頼できないため、サーバーで同じ(またはより徹底的な)検証を行います。
ハーゲンフォンアイゼン

3
@HagenvonEitzenしかし、技術スタックにもよりますが、重複する必要はありません。たとえば、ブラウザでJavaScriptチェック入力を行ってから、サーバーでJavaチェックを行うため、機能が重複しています。ただし、サーバーでNode.jsを実行する場合は、ブラウザーとサーバーで同じJavaScript検証を使用して、重複を排除できます。それでもコードを複数の場所(信頼できる環境と信頼できない環境)で実行する必要がありますが、コードを必ずしも複製する必要はありません。
ジョシュアテイラー

87

サンディメッツ、Rubyの生態系で有名なソフトウェアエンジニアと著者は、偉大持ちのブログ記事、彼女はまた、重複や抽象化との関係について語っています。彼女は次の結論に達します

複製は間違った抽象化よりもはるかに安価です

そして、私は彼女に完全に同意します。この引用文の背景を詳しく説明します。適切な抽象化を見つけることは非常に難しい場合があります。このような場合にはそれだけのために行くたくなる任意の重複を減らすために抽象化。しかし、後であなたの抽象化がすべての場合に当てはまるわけではないことがわかるかもしれません。ただし、すべてを再度変更して別のルートに移動するのはコストがかかります(はるかに良い説明については、彼女の話を見てください!)。

そうです、私にとっては、特に今後の予定が不明で要件が変更される可能性がある場合に、複製を受け入れることが正しい決定である例外的なケースがあります。あなたの投稿から、私は現在多くの重複があると思いますが、あなたの同僚はこれが変わる可能性があり、両方のアプリケーションを互いに結合しないことを示唆しています。私の意見では、これは有効な議論であり、一般的に無視することはできません。


23
ああ、一般化されたルールを推測できない場合、それらを抽象化しようとしても失敗するだけです。また、通信が偶然である場合、短命になる可能性があります。
デュプリケータ

5
抽象化を配置した後に変更するのはコストがかかる場合がありますが、配置した後に重複を取り除くのはさらに面倒です。もちろん、これは言語などに大きく依存します。現代の強力な静的型システムは、抽象化の権利に大規模な変更を加えるのにも役立ちます。同期で重複機能を維持するが、型システムがはるかに(重複しているため、とあなたを助けることができるものではありません型システムに、単に異なります)。しかし、動的なアヒル型言語では、逆の方向にあると思います。そのため、Rubyにとって意味があるかもしれません。
左辺

34

人々が「if tomorrow」という言葉でデザインについて推論を始める場合、これは多くの場合、私にとって大きな警告サインです。特に議論が余分な仕事と努力を含む決定を正当化するために使用される場合返済し、反対の決定よりも変更や復帰が難しい。

コードの複製は、短時間だけ労力を削減しますが、コードの複製行の数に比例して、ほぼすぐに保守作業を増加させます。また、コードが複製されると、これが間違った決定であることが判明したときに複製を削除するのが難しくなることに注意してください。間違った決定でした。

大規模な組織では、DRY原則よりもさまざまなチームの独立性を優先することが有益な場合があると述べました。API 2の95%の共通部分を抽出して重複を削除する場合、新しいコンポーネントが2つの独立したチームの結合につながる場合、これは最も賢明な決定ではないかもしれません。一方、リソースが限られていて、両方のAPIを保守しているチームが1つだけの場合、二重の努力を行わず、不必要なコードの重複を回避することが彼ら自身の利益になると確信しています。

さらに、「Home」および「Agency」APIが完全に異なるアプリケーションによって排他的に使用される場合、または「Home」コンテキストで使用できるAPIの上にコンポーネントビルドを作成しようとする場合、違いが生じることに注意してください「代理店」コンテキストのように。この状況では、APIの共通部分がまったく同じである(共通部分が複製されていない場合にのみ保証できます)ことにより、そのようなコンポーネントの開発がおそらくはるかに簡単になります。

したがって、実際には異なるサブチームが存在し、各サブチームが各APIを担当し、各チームが異なるスケジュールとリソースを持つ場合、「万が一の場合」ではなく、コードを複製するときです。


3
私にとって「もし明日」よりも大きな警告サインは「それは変わらないだろう」です。
abuzittin gillifirca

@abuzittingillifirca:そして、これは質問や私の答えと何の関係がありますか?
Doc Brown

1
最初の段落に挑戦しています。
abuzittin gillifirca

2
@abuzittingillifirca:さて、もう一度質問を読んでください:それは元に戻すの難しい複製の決定を正当化するための「明日」の引数で推論することなので、特定の場合にソフトウェアを実際に変更するのを難しくします。少し直観に反するかもしれませんが、将来のためにソフトウェアを変更可能に保つ最良の方法は、将来について(おそらく間違った)仮定をするのではなく、ソフトウェアをできるだけ小さく、安定した状態に保つことです。
Doc Brown

13

カップリングを防ぐための複製。2つの大きなシステムがあり、それらに同じライブラリを使用するように強制するとします。両方のシステムのリリースサイクルを結合している可能性があります。これはそれほど悪くないかもしれませんが、あるシステムが変更を導入する必要があるとしましょう。もう一方は変更を分析する必要があり、影響を受ける可能性があります。時々それは物事を壊すかもしれません。両者が変更を調整できたとしても、マネージャー、テスト、依存関係、小規模な自律チームの終わりを経て、多くの会議になる可能性があります。

そのため、自律性と独立性を得るために、重複したコードの代価を払っています。


したがって、これは結合を回避するためのコンポーネント間複製です。しかし、コンポーネント内での重複は避けたいと思いますか?
TemplateRex

ええ、そうです。コードの理解と変更が簡単になるので、重複するコードを最小限に抑える必要があります。しかし、実行可能な例外を含む良い答えがあることを覚えておいてください。重複を避けるための正しい抽象化を見つけるのは難しいかもしれません。
ボルジャブ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.