重複コードの存在がソフトウェア開発と保守に悪影響を与えることはよく知られています。実際、大きな欠点の1つは、バグ修正や新機能追加のために重複コードの1か所を変更した場合、対応する他の箇所も同時に変更しなければならないことです。
重複コードの最も一般的な原因はコピー&ペースト操作で、2か所以上にまったく同じソースコードが存在する状態です。この慣行は多くの記事、書籍、Webサイトで推奨されていません。しかし、推奨事項に従うのは必ずしも容易ではなく、開発者はしばしば簡単な解決策、つまりコピー&ペーストを選んでしまいます。
この種のクローンコードを検出するツールは多数あります。 CCFinderX は、利用可能な興味深いオープンソースツールの1つです。CCFinderXは、Java、C/C++、COBOL、VB、C#で書かれたソースファイル内のコードクローン(重複したコード断片)を検出するコードクローン検出ツールです。ユーザー側でプリプロセッサをカスタマイズでき、インタラクティブなメトリクスベースの解析を提供します。
適切なツールを使えば、コピー&ペースト操作によって生じた重複コードは簡単に検出できます。しかし、クローンコードの検出が容易ではない場合もあります。
隠れた重複コードケース1:変更されたコピー&ペーストコード
前述のとおり、コピー&ペーストコードの主な問題は、重複コードの1か所を変更した場合、対応する他の箇所も同時に変更しなければならないことです。残念ながら、常にそうなるとは限らず、重複コードの各インスタンスが異なっていくことがあります。
この種の隠れた重複コードを避けるには、CCFinderXのようなツールを使って重複コードのインスタンスを見つけ、コードをリファクタリングする時間がない場合でも、少なくともコメントを追加してタグ付けしてください。これは、開発者が重複コードのインスタンスを変更しようとしたときに、同じコードが他の場所にも存在することを知らせるうえで非常に役立ちます。一方、開発者が重複に気づかなければ、1つのインスタンスだけを変更してしまう可能性があり、後で変更された重複コードを検出するのが非常に困難になります。
ケース2:類似した機能
コピー&ペースト操作だけが重複コードの原因ではありません。もう1つの原因は、類似した機能が独立して実装されることです。
この2つ目の重複コードの原因について、 Wikipediaによる簡単な説明は次のとおりです:
Functionality that is very similar to that in another part of a program is required and a developer independently writes code that is very similar to what exists elsewhere. Studies suggest, that such independently rewritten code is typically not syntactically similar.隠れた重複コードを追跡する
重複コードがまったく同じではない場合、どのツールも信頼できる結果を提供できません。ツールは潜在的な重複コードを報告できるだけであり、それが本当にクローンコードなのか、単なる誤検出なのかを確認するのは開発者の責任です。
各ツールは、この種の重複コードを追跡するために固有のアルゴリズムを使用しています。私たちはこれらのツールをすべてテストしたわけではありませんが、そのほとんどは少なくとも一度試す価値があると思います。この記事の後半で見るように、コードの設計と実装を改善するのに役立つ興味深い結果が得られる可能性があります。
ここでは、 同じメンバーを使用するメソッドの集合、つまり同じメソッドを呼び出し、同じフィールドを読み取り、同じフィールドへ書き込むメソッドの集合を定義するアルゴリズムを使用します。これらの集合を suspect-setと呼びます。suspect-setは、使用している共通メンバーの数でソートされます。
CppDepend は、このアルゴリズムをCppDepend Power-Toolとして実装しています。Power-Toolsは、CppDepend.APIを基盤とするオープンソースツール群です。Power-Toolsのソースコードは次の場所にあります: $CppDependInstallPath$\CppDepend.PowerTools.SourceCode\CppDepend.PowerTools.sln。
このアルゴリズムが、Irrlicht 3Dエンジンのコードベースで重複コードを見つけるうえでどれほど効果的かを見てみましょう。
ケーススタディ:Irrlicht 3Dエンジン
The Irrlicht Engineは、C++で書かれたオープンソースの高性能リアルタイム3Dエンジンです。完全にクロスプラットフォームです。
検出された疑わしい重複コードの例を2つ示します:
1. 完全に同一の重複コード
このケースでは、検出された18個のメソッドが同じ3つのメソッドを使用し、同じ2つのフィールドを読み取り、同じ9つのフィールドへ書き込んでいます。

これらのメソッドのソースコードを確認したところ、完全に同一の重複コードが含まれていることが分かりました。ただし、この種の重複の検出には他のツールの方が適しており、完全に同一のクローンの検出に関して、このアルゴリズムが追加の価値を提供するわけではありません。
2. 類似した機能
2つ目の疑わしい重複を示します。これは、同じ11個のメソッドを使用し、同じ6つのフィールドを読み取り、同じ2つのフィールドへ書き込む4つのメソッドに関係しています。

これら4つのメソッドのソースコードを確認したところ、コードはまったく同じではありませんでした。しかし、同じレイアウトアルゴリズムを実装しているため、このケースではリファクタリングを推奨します。
このケースをより分かりやすく説明するために、重複コードに関係するクラス間の関係を示します:

OnSetConstantsはIShaderConstantSetCallBackインターフェースで宣言され、すべての派生クラスで実装されています。4つの実装はすべて同じレイアウトアルゴリズムを持ち、このような場合には テンプレートメソッドパターン が既存実装をリファクタリングするための良い解決策になります。
このアルゴリズムを多くのオープンソースC++プロジェクトでテストしたところ、重複コードの多くがこのケースに似ていること、そしてテンプレートメソッドパターンがほとんど使われていないことに非常に驚きました。
結論
重複コードの追跡は、プロジェクトの実装と設計の両方を改善するうえで非常に有用です。幸い、クローンコードを検出するツールは多数存在します。これらのツールの1つを定期的に実行し、少なくとも重複しているインスタンスにタグを付けるのは良い考えです。
