幻のコードの複製


56

通常の本能は、コードに表示されるコードの重複を削除することです。しかし、複製が幻想的である状況にいることに気づきました

より詳細に状況を説明するために:私はWebアプリケーションを開発していますが、ほとんどのビューは基本的に同じです-ユーザーがスクロールして選択できるアイテムのリスト、選択したアイテムを含む2番目のリスト、および「保存ボタンをクリックして、新しいリストを保存します。

問題は簡単なように思えました。ただし、それぞれのビューには独自の癖があります-場合によっては何かを再計算する必要があり、時には追加のデータなどを保存する必要があります。これらは、メインロジックコードにコールバックフックを挿入することで解決しました。

基本的にすべての機能にコールバックを提供する必要があり、メインロジックはコールバック呼び出しの巨大なシーケンスのように見えるため、ビューには非常に多くの微細な違いがあります。すべてのビューには実行される独自のコードがあり、すべてコールバックで実行されるため、最終的には時間やコードを節約しません。

問題は次のとおりです。

  • 違いは非常に小さいため、コードはすべてのビューでほぼ同じように見えますが、
  • 非常に多くの違いがあるので、詳細を見るとき、コードを書くことは少し似ていません

この状況にどのように対処すればよいですか?
コールバックコールのみで構成されたコアロジックを持つことは良い解決策ですか?
または、コードを複製して、コールバックベースのコードの複雑さをなくすべきですか?


38
私は通常、複製を最初に手放すことが役立つと思います。いくつかの例を挙げれば、一般的なものとそうでないものを見やすくなり、共通部分を共有する方法を考え出すことができます。
ウィンストンイーバート

7
非常に良い質問-考慮すべきことの1つは、コードの物理的な複製だけでなく、意味的な複製です。コードの一部の変更が必然的に同じ変更が他のコード全体に複製されることを意味する場合、その部分はおそらくリファクタリングまたは抽象化の候補です。時々、実際に自分自身をトラップするポイントに正規化できるので、重複を意味的に異なるものとして扱うための実用的な意味も考慮します。
Ant P

同じことを行うコードのみを再利用できることを忘れないでください。アプリが異なる画面で異なる処理を行う場合、異なるコールバックが必要になります。それについてifs、ands、またはbutsはありません。
corsiKa

13
これに関する私の個人的な経験則は、ある場所でコードを変更した場合、他のどこでもまったく同じ変更を行わないとバグですか?もしそうなら、それは悪い種類の複製です。よくわからない場合は、現時点で読みやすい方を選択してください。あなたの例では、振る舞いの違いは意図的なものであり、バグとは見なされないため、重複は問題ありません。
Ixrec

アスペクト指向プログラミングについて読むことに興味があるかもしれません。
ベンジャクソン

回答:


53

最終的には、同様のコードを組み合わせて重複を排除するかどうかについて判断する必要があります。

不幸な傾向として、「繰り返してはいけない」などの原則を、常にローテが従わなければならないルールとして採用しているようです。実際、これらは普遍的なルールではなく、優れたデザインについて考え、開発するのに役立つガイドラインです。

人生のすべてとして、あなたは利益対費用を考慮しなければなりません。重複コードはどれだけ削除されますか?コードは何回繰り返されますか?より一般的なデザインを書くのにどれだけの労力がかかりますか?将来、どのくらいコードを開発する予定ですか?等々。

特定のコードを知らなければ、これは不明確です。おそらく、重複を削除するよりエレガントな方法があるでしょう(LindaJeanneが提案した方法など)。または、抽象化を正当化するだけの本当の繰り返しが存在しない可能性があります。

設計への不十分な注意は落とし穴ですが、過剰設計にも注意してください。


「不幸な傾向」と盲目的にガイドラインに従うことについてのあなたのコメントはその場にあると思う。
マエル

1
@Mael このコードを将来維持しなければ、適切なデザインを取得する正当な理由がないと言っていますか?(違反はありません、あなたがそれについてどう思うか知りたいだけです)
発見

2
@Maelもちろん、それは単なる不幸な言い回しだと考えることができます!:Dただし、コードを書くときは、他の人と同じように自分自身に厳しくするべきだと思います(コードを書いてから2週間後に自分のコードを読むとき私は自分を他人と見なします)。
発見

2
@ user61852を使用すると、コードレスコードが非常に嫌になります。
ラバーダック

1
@ user61852、笑-しかし、それすべて(質問で与えられていない情報に依存している場合)過剰な確実性ほど役立つものはほとんどありません。

43

DRYは知識に関するものであることを忘れないでください。2つのコードが似ているか、同一であるか、まったく異なるかは問題ではありません。システムに関する同じ知識がそれらの両方で見つかれば、重要なことです。

知識の一部は、事実(「意図した値からの最大許容偏差は0.1%」)またはプロセスの一部(「このキューには3つ以上の項目が含まれない」)である可能性があります。基本的に、ソースコードにエンコードされた情報の単一の断片です。

したがって、削除すべき重複があるかどうかを判断するときは、それが知識の重複であるかどうかを尋ねます。そうでない場合、おそらく偶発的な複製であり、一般的な場所に抽出すると、後で明らかにその複製された部分が異なる同様のコンポーネントを作成するときに問題が発生します。


12
この!DRYの焦点は、重複した変更を避けることです。
マチューM.

これは非常に役立ちます。

1
DRYの焦点は、同じ振る舞いをするべきではない2つのコードが存在しないようにすることだと思いました。コードの変更を2回適用する必要があるため、問題は2倍の仕事ではありません。実際の問題は、コードの変更を2回適用する必要があるが、そうでない場合です。
gnasher729

3
@ gnasher729うん、それがポイントです。2つのコードに知識の重複がある場合、一方を変更する必要がある場合、他方も変更する必要があり、説明する問題が発生することが予想されます。彼らが持っている場合は偶発的な重複を 1に変更する必要があるとき、そして、他のはよく同じ滞在する必要があるかもしれません。その場合、あなたは一般的な方法(または何でも)で抽出した場合、あなたが今に対処するためのさまざまな問題を抱えている
ベン・アーロンソン

1
また、基本的な重複不慮の重複、参照Rubyで偶然ドッペルゲンガーをし、I DRY-EDアップマイコードと今ではと仕事をするのは難しいです。どうした?。また、コンテキスト境界の両側で偶発的な重複が発生します。概要:クライアントがこれらの依存関係を同時に変更することが理にかなっている場合にのみ、重複をマージします。
エリック

27

戦略パターンの使用を検討しましたか?複数のビューによって呼び出される共通のコードとルーチンを含む1つのViewクラスがあります。Viewクラスの子には、それらのインスタンスに固有のコードが含まれます。それらはすべて、ビュー用に作成した共通インターフェースを使用するため、違いはカプセル化され、一貫性があります。


5
いいえ、考えていません。ご提案ありがとうございます。戦略パターンについて簡単に読んだところ、私が探しているもののように思えます。私は間違いなくさらに調査します。
マエル

3
そこにあるTemplate Methodパターンは。また、それを考慮することができます
シャキル

5

変更の可能性は何ですか?たとえば、このアプリケーションには8つの異なるビジネスエリアがあり、各エリアに4つ以上のユーザータイプの可能性があります。ビューは、ユーザーの種類と領域に基づいてカスタマイズされます。

最初は、これは同じビューを使用して行われ、さまざまなものを表示するかどうかを決定するために、あちこちでいくつかのチェックが行われました。時間が経つにつれて、いくつかのビジネスエリアは劇的に異なることをすることにしました。最終的に、ビジネスエリアごとの機能ごとに1つのビュー(ASP.NET MVCの場合は部分ビュー)に基本的に移行しました。すべてのビジネスエリアが同じ機能を備えているわけではありませんが、他のビジネスエリアが持っている機能が必要な場合、そのエリアには独自のビューがあります。コードの理解とテスト容易性の面倒さがはるかに少なくなります。たとえば、ある領域に変更を加えても、別の領域に不要な変更が生じることはありません。

@ dan1111が述べたように、それは判断の電話になります。やがて、それが機能するかどうかがわかるかもしれません。


2

1つの問題は、単一レベルの機能のみにインターフェース(理論上のインターフェースであり、言語機能ではない)を提供していることです。

A(a,b,c) //a,b,c are your callbacks or other dependencies

必要な制御量に応じて、複数のレベルではなく:

//high level
A(a,b,c)
//lower
A myA(a,b)
B(myA,c)
//even lower
A myA(a)
B myB(myA,b)
C myC(myB,c)
//all the way down to you just having to write the code yourself

私が理解した限りでは、あなたは高レベルのインターフェース(A)のみを公開し、実装の詳細(他のこと)を隠しています。

実装の詳細を非表示にすることには利点があり、欠点を見つけました。低レベルのインターフェースを直接使用する場合に可能なすべての機能を明示的に追加しない限り、制御は制限されます。

したがって、2つのオプションがあります。低レベルのインターフェイスのみを使用するか、高レベルのインターフェイスを維持するには作業が多すぎるため低レベルのインターフェイスを使用するか、高レベルと低レベルの両方のインターフェイスを公開します。唯一の賢明なオプションは、冗長なコードを避けたいと仮定して、高レベルと低レベルの両方のインターフェース(およびその間のすべて)を提供することです。

次に、別のものを作成するときは、これまでに作成したすべての使用可能な機能(無数の可能性、どの機能を再利用するかを決定するまで)を確認し、それらをつなぎ合わせます。

制御がほとんど必要ない場合は、単一のオブジェクトを使用します。

何らかの異常が発生する必要がある場合は、最低レベルの機能を使用します。

また、あまり白黒ではありません。たぶん、あなたの大きな高レベルのクラスは、可能なすべてのユースケースを合理的にカバーできます。ユースケースは非常に多様であるため、最低レベルのプリミティブ機能で十分です。バランスを見つけるのはあなた次第です。


1

他の有用な答えがすでにあります。私を追加します。

複製が悪いのは

  1. それはコードを乱雑にします
  2. それはコードの理解を混乱させますが、最も重要なのは
  3. ここで何かを変更し、そこでも何かを変更する必要がある場合は、忘れたりバグを導入したりする可能性があるため、決して忘れることはできません。

重要なのは、それのために、または誰かがそれが重要だと言ったからといって、重複を排除していないということです。バグ/問題を減らしたいので、あなたはそれをやっています。あなたの場合、ビューで何かを変更しても、他のすべてのビューでまったく同じ行を変更する必要おそらくないようです。したがって、実際の複製ではなく、明らかな複製があります。

もう1つの重要なポイントは、ジョエルが言ったように、原則の問題だけに基づいて現在動作しているものをゼロから書き直さないことです(既に彼のことを聞いていたかもしれません...)。したがって、ビューが機能している場合は、一歩一歩改善を進め、「ソフトウェア会社が犯す可能性のある単一の最悪の戦略的ミス」の犠牲にならないようにしてください。

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