未知のコードの重複を防ぐにはどうすればよいですか?


33

私はかなり大きなコードベースに取り組んでいます。数百のクラス、大量の異なるファイル、多くの機能が、新しいコピーの取得などに15分以上かかります。

このような大きなコードベースの大きな問題は、かなりの数のユーティリティメソッドがあり、同じことを行うか、可能な場合にこれらのユーティリティメソッドを使用しないコードがあることです。また、ユーティリティメソッドは、すべてが1つのクラスに含まれているわけではありません(巨大な混乱のせいだからです)。

私はコードベースにはかなり慣れていませんが、何年もこのコードに取り組んでいるチームリーダーも同じ問題を抱えているようです。それは多くのコードと作業の重複につながり、そのため、何かが壊れると、通常は基本的に同じコードの4つのコピーで壊れます

このパターンをどのように抑制できますか?ほとんどの大規模プロジェクトと同様に、すべてのコードが文書化されているわけではありません(一部は文書化されています)。しかし、基本的には、この点で品質の改善に取り組み、将来コードの重複を減らし、ユーティリティ関数のようなものを見つけやすくすることができれば、本当に素晴らしいことです。

また、ユーティリティ関数は通常、いくつかの静的ヘルパークラス、単一のオブジェクトで動作する非静的ヘルパークラス、または主に「ヘルプ」を行うクラスの静的メソッドのいずれかにあります。

拡張メソッドとしてユーティリティ関数を追加する1つの実験がありました(クラスの内部は不要であり、非常に特定のシナリオでのみ必要でした)。これには、プライマリクラスなどが乱雑になるのを防ぐ効果がありましたが、すでにそれを知っていない限り、実際には発見できません


回答:


30

簡単な答えは、コードの重複を本当に防ぐことはできないということです。ただし、次の2つのステップに要約される困難で継続的な反復増分プロセスによって「修正」できます。

ステップ1.レガシーコードのテストの作成を開始します(テストフレームワークを使用することが望ましい)

ステップ2.テストから学んだことを使用して、複製されたコードを書き換え/リファクタリングします

静的解析ツール使用して、重複したコードを検出できます。C#には、これを実行できるツールがたくさんあります。

このようなツールは、同様のことを行うコード内のポイントを見つけるのに役立ちます。テストを書き続けて、実際にテストが行​​われることを確認します 同じテストを使用して、複製コードを使いやすくします。この「リファクタリング」は複数の方法で実行でき、このリストを使用して正しいリストを決定できます。

さらに、Michael C. Feathersによるこのトピックに関する本全体が、レガシーコードで効果的に機能しています。コードをより良いものに変更するために取ることができるさまざまな戦略を深く掘り下げます。彼は、上記の2ステップのプロセスからそれほど遠くない「レガシーコード変更アルゴリズム」を持っています。

  1. 変化点を特定する
  2. テストポイントを見つける
  3. 依存関係を解除する
  4. テストを書く
  5. 変更を加えてリファクタリングする

この本は、ブラウンフィールド開発、つまり変更が必要なレガシーコードを扱っている場合に役立ちます。

この場合

OPの場合、テストできないコードは、いくつかの形式をとる「ユーティリティメソッドとトリック」のハニーポットが原因であると想像できます。

これらには何の問題もないことに注意してください。しかし、一方で、それらは通常、維持や変更が困難です。.NETの拡張メソッドは静的メソッドですが、テストも比較的簡単です。

ただし、リファクタリングを行う前に、それについてチームに相談してください。何かを進める前に、あなたと同じページにそれらを保持する必要があります。これは、何かをリファクタリングしている場合、可能性が高いため、マージの競合が発生するためです。そのため、何かをやり直す前に、それを調査し、完了するまでしばらくの間、これらのコードポイントに注意して作業するようチームに伝えてください。

OPはコードにとって新しいものであるため、何かを行う前に行うべきことがいくつかあります。

  • コードベースから時間をかけて学習します。つまり、「すべて」を壊し、「すべて」をテストし、元に戻します。
  • コミットする前に、チームの誰かにコードのレビューを依頼してください。;-)

がんばろう!


実際には、ユニットテストと統合テストがかなり行われています。100%のカバレッジではありませんが、コードベースを根本的に変更しない限り、ユニットテストを行うことはほとんど不可能です。重複を見つけるために静的解析を使用することは考えませんでした。次に試してみる必要があります。
アールズ

@Earlz:静的コード分析は素晴らしいです!;-)また、変更を行う必要があるときはいつでも、変更を簡単にする解決策を考えてください(パターンカタログのリファクタリングを確認してください)
Spoike

+1誰かがこのQに賞金をかけて、このアンサーを「非常に役立つ」と評価してもらえると思います。パターンカタログへのリファクタリングは、の形でこのようなもの、金であるGuidanceExplorer.codeplex.comは偉大なプログラミングを補助しています。
ジェレミートンプソン

2

問題を別の角度から見ることもできます。問題がコードの重複であると考える代わりに、問題の原因がコードの再利用に関するポリシーの欠如にあるかどうかを検討できます。

最近、「再利用可能なコンポーネントを使用したソフトウェアエンジニアリング」という本を読みましたが、実際、組織レベルでコードの再利用性を促進する方法に関する非常に興味深いアイデアがあります。

この本の著者であるJohannes Sametingerは、コードの再利用に対する一連の障壁について説明しています。例えば:

概念的および技術的

  • 再利用可能なソフトウェアを見つけるのが難しい:ソフトウェアは、見つからない限り再利用できません。リポジトリにコンポーネントに関する十分な情報がない場合、またはコンポーネントの分類が不十分な場合、再利用は起こりそうにありません。
  • 見つかったソフトウェアの非再利用性:既存のソフトウェアに簡単にアクセスできると、必ずしもソフトウェアの再利用が増えるとは限りません。意図せずに、ソフトウェアが他の人が再利用できるように書かれていることはめったにありません。必要な機能をゼロからプログラミングするよりも、他の人のソフトウェアを変更して適応させることはさらに高価になる可能性があります。
  • 再利用に適さないレガシーコンポーネント:再利用のために設計および開発されていない限り、コンポーネントの再利用は困難または不可能です。さまざまなレガシーソフトウェアシステムから既存のコンポーネントを収集し、それらを新しい開発に再利用しようとするだけでは、体系的な再利用には不十分です。リエンジニアリングは、再利用可能なコンポーネントの抽出に役立ちますが、かなりの労力が必要になる場合があります。
  • オブジェクト指向技術:オブジェクト指向技術はソフトウェアの再利用に良い影響を与えると広く信じられています。残念なことに、多くの人は、再利用はこの技術に依存するか、オブジェクト指向技術の採用でソフトウェアの再利用に十分であると考えています。
  • 変更:コンポーネントは、必ずしも正確に私たちが望むようになるとは限りません。変更が必要な場合は、コンポーネントへの影響とその以前の検証結果を判断できるはずです。
  • ガベージの再利用:再利用可能なコンポーネントを特定の品質レベルに認定することで、起こりうる欠陥を最小限に抑えることができます。品質管理の悪さは、再利用の大きな障壁の1つです。必要な機能がコンポーネントによって提供される機能と一致するかどうかを判断する手段が必要です。

その他の基本的な技術的な問題は次のとおりです。

  • 再利用可能なコンポーネントの構成に同意する。
  • コンポーネントの機能と使用方法を理解する。
  • 再利用可能なコンポーネントを設計の残りの部分に接続する方法を理解する。
  • 管理された方法で簡単に適応および変更できるように、再利用可能なコンポーネントを設計します。
  • プログラマーが必要なものを見つけて使用できるようにリポジトリを編成します。

著者によると、組織の成熟度に応じて、さまざまなレベルの再利用性が発生します。

  • アプリケーショングループ間のアドホックな再利用:再利用への明確なコミットメントがない場合、再利用は非公式で無計画な方法で行われます。再利用のほとんどは、プロジェクト内で行われます。また、これはコードの清掃につながり、コードの重複につながります。
  • アプリケーショングループ間でのリポジトリベースの再利用:コンポーネントリポジトリが使用され、さまざまなアプリケーショングループがアクセスできる場合、状況はわずかに改善されます。ただし、コンポーネントをリポジトリに配置するための明示的なメカニズムは存在せず、リポジトリ内のコンポーネントの品質について責任を負う者はいません。これは多くの問題につながり、ソフトウェアの再利用を妨げる可能性があります。
  • コンポーネントグループによる集中再利用:このシナリオでは、コンポーネントグループがリポジトリを明示的に担当します。このグループは、リポジトリに保存するコンポーネントを決定し、これらのコンポーネントの品質と必要なドキュメントの可用性を確保し、特定の再利用シナリオで適切なコンポーネントを取得するのに役立ちます。アプリケーショングループは、各アプリケーショングループの一種の外注先として機能するコンポーネントグループから分離されています。コンポーネントグループの目的は、冗長性を最小限にすることです。一部のモデルでは、このグループのメンバーは特定のプロジェクトに取り組むこともできます。プロジェクトの開始時には、彼らの知識は再利用を促進するために貴重であり、特定のプロジェクトへの関与のおかげで、リポジトリに含める可能性のある候補を特定できます。
  • ドメインベースの再利用:コンポーネントグループの専門化は、ドメインベースの再利用になります。各ドメイングループは、そのドメイン内のコンポーネント(ネットワークコンポーネント、ユーザーインターフェイスコンポーネント、データベースコンポーネントなど)を担当します。

したがって、おそらく、他の回答で提供されたすべての提案に加えて、再利用プログラムの設計、管理に関与し、ドメイン分析を行って再利用可能なコンポーネントを特定するコンポーネントグループを形成し、他の開発者が簡単に使用できる再利用可能なコンポーネントのリポジトリを定義できますクエリを実行し、問題に対する解決策を探します。


1

2つの解決策があります。

防止 -可能な限り適切なドキュメントを作成してください。すべての機能を適切に文書化し、文書全体を検索しやすくします。また、コードを書くときは、コードがどこに行くべきかを明確にしてください。「ユーティリティ」コードの量を制限することは、この重要なポイントの1つです。「ユーティリティクラスを作ろう」と聞くたびに、髪が上がり、血液が凍ります。これは明らかに問題です。いくつかの機能が既に存在する場合は常に、コードベースを知るように人々に依頼するための迅速かつ簡単な方法が常にあります。

解決策 -予防に失敗した場合、問題のあるコードを迅速かつ効率的に解決できるはずです。開発プロセスでは、重複コードを迅速に修正できる必要があります。コードを壊すことを恐れずに効率的にコードを変更できるため、単体テストはこれに最適です。したがって、2つの同様のコードが見つかった場合、それらを関数またはクラスに抽象化することは、リファクタリングを少し行うだけで簡単になります。

個人的には、予防は可能だとは思いません。試行すればするほど、既存の機能を見つけるのが難しくなります。


0

この種の問題に一般的な解決策があるとは思わない。開発者が既存のコードを検索するのに十分な意思がある場合、重複したコードは作成されません。また、開発者は必要に応じてその場で問題を修正できます。

言語がC / C ++である場合、リンクの柔軟性のために、マージは簡単になります(extern事前情報なしで関数を呼び出すことができます)。Javaまたは.NETの場合、ヘルパークラスやユーティリティコンポーネントを考案する必要がある場合があります。

通常、重複した部分から大きなエラーが発生した場合にのみ、既存のコードの重複除去を開始します。


0

これは、多くのプログラマーによって処理されている大規模プロジェクトの典型的な問題であり、時には多くの仲間からの圧力の下で貢献しています。クラスのコピーを作成し、その特定のクラスに適合させることは非常に魅力的です。ただし、元のクラスで問題が見つかった場合は、忘れられがちな子孫でも解決する必要があります。

これには解決策があり、Java 6で導入されたジェネリックと呼ばれます。これは、テンプレートと呼ばれるC ++に相当します。正確なクラスが汎用クラス内でまだ知られていないコード。Java Genericsを確認してください。多数のドキュメントが見つかります。

良いアプローチは、特定のバグのために修正する必要がある最初のコードを書き換えることにより、多くの場所でコピー/貼り付けされているように見えるコードを書き換えることです。Genericsを使用するように書き直し、非常に厳密なテストコードも記述します。

Genericクラスのすべてのメソッドが呼び出されることを確認してください。また、コードカバレッジツールを導入することもできます。汎用コードはいくつかの場所で使用されるため、完全にコードカバレッジにする必要があります。

また、テストコードを作成します。つまり、Genericコードと組み合わせて使用​​する予定の最初に指定されたクラスに対して、JUnitなどを使用します。

上記のすべてのコードが機能し、完全にテストされたら、2番目の(ほとんどの場合)コピーされたバージョンの汎用コードの使用を開始します。その指定されたクラスに固有のコード行がいくつかあることがわかります。Genericベースクラスを使用する派生クラスで実装する必要がある抽象保護メソッドでコーディングされたこれらの行を呼び出すことができます。

はい、それは退屈な仕事ですが、あなたがそれに沿って進むと、同様のクラスをリッピングし、非常にきれいで、よく書かれており、メンテナンスがはるかに簡単なものに置き換えることがますます良くなります。

私は似たような状況に陥り、ジェネリッククラスで最終的に6または7のような他のほぼ同一のクラスを置き換えました。これらはすべてほぼ同じですが、さまざまなプログラマーによって一定期間コピーおよび貼り付けられました。

そして、はい、私はコードの自動テストに非常に賛成です。最初はコストがかかりますが、全体の時間を大幅に節約できます。また、汎用コードでは、全体で少なくとも80%および100%のコードカバレッジを達成するようにしてください。

これが助けて、幸運を願っています。


0

私は実際、ここで最も人気のない意見をエコーし​​、Gangnusコードの重複が常に有害であるとは限らず、時にはより小さな悪である可能性があることを示唆します。

たとえば、次のオプションを使用できる場合:

A)よくテストされた安定した(変化のない)小さな画像ライブラリで、ドット積やlerpsやclampsのようなベクトル数学用の数十行の些細な数学コードを複製しますが、他のものから完全に切り離され、ほんの数分の一で構築されます二番目の。

B)上記の数十行のコードを回避するために壮大な数学ライブラリに依存する不安定な(急速に変化する)イメージライブラリ。数学ライブラリは不安定であり、常に新しい更新と変更を受け取るため、イメージライブラリも完全に変更されていない場合も再構築されます。全体をクリーンビルドするには15分かかります。

...それから、A、そして実際には正確にその小さなコードの重複のために、それが望ましいことはほとんどの人にとって明白なはずです。強調する必要がある重要な点は、十分にテストされた部分です。明らかに、そもそも動作しないコードを複製することほど悪いことはありません。その時点で、それはバグを複製しています。

しかし、考慮すべき結合と安定性もあり、いくつかの控えめな複製は、パッケージの安定性(変化しない性質)を高める分離メカニズムとして機能します。

だから、私の提案は実際にテストにもっと焦点を当て、本当に安定したもの(変化しない、将来変化するいくつかの理由を見つけるなど)と信頼できる外部ソースへの依存関係があればそれを考え出すことです非常に安定しており、コードベース内のすべての形式の複製を排除しようとしています。大規模なチーム環境では、後者は非現実的な目標になる傾向があります。もちろん、コードベース内のカップリングと不安定なコードの量を増やす可能性があることは言うまでもありません。


-2

コードの重複は必ずしも有害ではないことを忘れないでください。想像してみてください。今、あなたはあなたのプロジェクトの全く異なるモジュールで解決すべきいくつかのタスクを持っています。今は同じタスクです。

それには3つの理由があります。

  1. このタスクに関するいくつかのテーマは、両方のモジュールで同じです。この場合、コードの複製は不良であり、清算する必要があります。このテーマをサポートするクラスまたはモジュールを作成し、両方のモジュールでそのメソッドを使用するのが賢明でしょう。

  2. タスクは、プロジェクトの観点から理論的なものです。たとえば、物理学や数学などからのものです。タスクはプロジェクトに独立して存在します。この場合、コードの複製は不良であり、清算する必要があります。そのような関数用の特別なクラスを作成します。そして、必要なモジュールでこのような関数を使用します。

  3. しかし、他のケースでは、タスクの偶然の一致は一時的な偶然の一致であり、それ以上ではありません。リファクタリングやデバッグのために、プロジェクトの変更中にこれらのタスクが同じままであると信じることは危険です。この場合、異なる場所に2つの同じ関数/コードを作成することをお勧めします。そして、それらの1つでの将来の変更は、もう1つに影響しません。

そして、この3番目のケースは非常に頻繁に発生します。「知らないうちに」複製する場合、ほとんどがまさにこの理由のためです-それは本当の複製ではありません!

ですから、本当に必要なときはきれいに保ち、それが必要でなければ複製を恐れないでください。


2
code duplication is not always harmful一つの悪いアドバイスです。
Tulainsコルドバ

1
私はあなたの権威に屈するべきですか?ここに自分の理由を書いた。間違っている場合は、どこに間違いがあるかを示してください。今では、議論を続ける能力が乏しいようです。
ガンヌス

3
コードの重複はソフトウェア開発の中心的な問題の1つであり、多くのコンピューティング科学者や理論家は、ソフトウェア開発の保守性の問題の主な原因としてのコードの重複を避けるためのパラダイムと方法論を開発しました。「貧弱なコードを書くことは必ずしも悪いことではない」と言っているようなもので、そのように何かを修辞的に正当化することができます。たぶん、あなたは正しいですが、コードの重複を避けることは反対を奨励するようで生きてもいいの原則..です
Tulainsコルドバ

私がいるここに引数を置きます。あなたはしていません。当局への言及は16世紀以降機能しません。あなたがそれらを正しく理解していること、そして彼らが私に対する権威であることを保証することはできません。
ガンヌス

あなたは正しい、コードの重複ソフトウェア開発の中心的な問題の1つではなく、それを回避するためのパラダイムと方法論は開発されていません。
Tulainsコルドバ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.