コードクローン統合分析ツール ICCA 肥後 芳樹† ,吉田 則裕† ,神谷 年洋‡,楠本 真二† ,井上 克郎† †大阪大学 大学院情報科学研究科 {y-higo, n-yosida, kusumoto, inoue}@ist.osaka-u.ac.jp ‡科学技術振興機構 さきがけ kamiya@ist.osaka-u.ac.jp The title of my talk is “aries: refactoring support environment based on code clone analysis”
ICCA - 概要(1/2) GUI ベースの統合分析ツール CCFinder をコードクローン検出エンジンとして使用 目的別にサブコンポーネントが存在 可視化,理解支援 - Gemini コンポーネント リファクタリング支援 - Aries コンポーネント デバッグ支援 - Libra コンポーネント 実装言語 Java 規模 ファイル数 : 523 総行数 : 約136,000行
ICCA - 概要(2/2) Aries Gemini Libra Software Understanding Software Visualization Refactoring Support Code clone repository Subsystem User Code clone detector (CCFinder) ICCA (Integrated Code Clone Analyzer) Gemini Aries Libra Debug Support
クローンペア と クローンセット クローンペア クローンセット (C1, C4) {C1, C4, C5} (C1, C5) クローンペア: 互いに一致・類似しているコード片の対 クローンセット: 互いに一致・類似しているコード片の集合 以前はクローンクラスと言っていました CCFinder はコードクローンをクローンペアとして検出する CCFinder 配布セットには CCReformer というツールが含まれており,このツールを用いるとクローンペアをクローンセットに変換することができる クローンペア クローンセット (C1, C4) {C1, C4, C5} (C1, C5) {C2, C3} (C2, C3) (C4, C5) C1 C2 C3 C4 では,ここで私たちが用いているコードクローンの語句を紹介したいと思います. まず1つめがクローンペアです. クローンペアとは,互いに類似したコード片の対を表す言葉です. もう1つがクローンセットです. クローンセットとは,互いに類似しているコード片の集合を表す言葉です. 例えば,このソースコードではコード片C1,C4,C5が類似しており,C2とC3 が類似しています. この様な場合,クローンペアは(C1, C4)のペア,(C1, C5)のペア,(C2,C3)のペア,(C4,C5)の4つのペアが存在し, クローンセットは(C1, C4, C5)のセットと(C2,C3)のセットが存在します. CCFinderはクローンペアとしてコードクローンを検出し,そのあとクローンセット形式で出力します. C5
Gemini - 概要 目的 CCFinder の検出したコードクローンの可視化・理解支援 分析の分離 旧 Gemini : 全てのビューが密接に関連 新 Gemini : “クローンベースの分析”と”ファイルベースの分析” スケーラビリティの改善 旧 Gemini : 数万行(検出したクローンの量に依存) 新 Gemini : 数百万行(検出したクローンの量に依存)
Gemini - 分析の分離 旧 Gemini では全てのビューが密接に関連をしている 理解しにくい 分析を “クローンベースの分析” と “ファイルベースの分析” に分離 クローンベースの分析 メトリクスグラフ クローンセットリスト ソースコードビュー ファイルベースの分析 スキャタープロット ファイルリスト グループリスト ディレクトリツリー ソースコードビュー
Gemini - スキャタープロット a b c a b c a d e c a b c a b c a d e c 原点は左上角 水平軸,垂直軸はソースコードのトークン列を表す 黒い点は,両軸の対応したトークンが一致していることを表す 主対角線上では自己比較が行われるため,常に主対角線が描画される CCFinderから検出されてくるようなクローンペアは,対角線分として現れる a b c a b c a d e c a b c a b c a d e c a, b, c, ... : tokens : matched position
Gemini - メトリクスグラフ(1/3) メトリクスグラフで用いているメトリクス LEN(S) : クローンセット S に含まれるコード片のトークン数の平均値 POP(S) : S に含まれるコード片の数 DFL(S) : S に含まれる全てのコード片を1つのサブルーチンにマージした場合のトークン減少数の予測値 RAD(S) : S に含まれるコード片がファイルシステム内での分散度 全てのコード片が1つのファイル内に存在 : RAD(S) = 0 全てのコード片が1つのディレクトリ内に存在 : RAD(S) = 1 new sub routine caller statements
Gemini - メトリクスグラフ(2/3) メトリクスの続き RNR(S) : S に含まれるコード片がどの程度”非繰り返し”であるかを表す. 例 : 以下のトークン列を考える このトークン列から,CCFinder はコード片 F1とF2 がクローンになっていると検出する. この時 RNR(S) は以下の式で計算される
Gemini - メトリクスグラフ(3/3) パラレルコーディネーショングラフ 各メトリクス毎に平行座標軸 1つのクローンセットにつき,1本の折れ線を描画 RAD LEN POP DFL RNR 4 3 2 1
Gemini - スケーラビリティの改善 旧 Gemini において著しくスケーラビリティを低下させていたのはスキャタープロット(クローンペアデータ) スキャタープロットの改善 ズーム機能 旧 Gemini : 50倍が限度 新 Gemini : 1つのファイルにまで拡大可能 クローンのレンダリング 旧 Gemini :常に1つのクローンペアにつき,1本の線分を描画 新 Gemini : 状況に応じて,複数のクローンをまとめて描画 なくなった機能 ソーティング機能 旧 Gemini : ファイルID,クローン率などで並び替えが可能 新 Gemini : 並び替えは不可能
Gemini – 分析例(デモンストレーション) クローンベースの分析 1例 ファイルベースの分析 3例
Gemini - クローンベースの分析例 目的 クローンセットをその特徴に基づいて絞込みを行う 解析の流れ(例) メトリクスグラフを用いてクローンセットの絞込み クローンセットリストを用いて,絞り込んだクローンセットのソーティング ソースコードビューを用いて絞り込んだクローンのソースコード閲覧
Gemini - ファイルベースの分析例(1/2) 目的 ファイルをその情報に基づいて絞込みを行う 解析の流れ(例) 例1: ディレクトリツリーから ディレクトリツリーでファイル(またはディレクトリ)を選択 スキャタープロットでクローン分布状態を確認 ソースコードビューでファイルのソースコードを閲覧
Gemini - ファイルベースの分析(2/2) 解析の流れ(続き) 例2: ファイルリストから ファイルリストで定量的な値に基づき,ファイルを選択 選択されたファイルとクローンを共有しているファイルを確認 ソースコードビューで選択されたファイルのソースコードを閲覧 例3: グループリストから グループリストでクローン率が高いグループを選択 選択されたグループに含まれているファイルを確認 選択されたグループと他の各グループとの類似度を確認
Gemini - ファイルメトリクス NOL(f): ファイル f の行数 NOT(f): f のトークン数 NOC(f): f に含まれているコードクローンの数 ROC(f): f がどの程度クローンになっているかを表す 例: ファイル f1 は 200 トークンから成り,そのうちの 100 トークンがコードクローンになっている ROC(f1) = (100 / 200) × 100% = 50% NOF(f): f とクローンを共有しているファイルの数
Gemini – フラグメントリスト コードフラグメントの属性 ファイル内位置 開始行.開始列 - 終了行.終了列 長さ トークン数 分散度(ディレクトリ構造上どの程度広がっているか) Dense: 全てのクローンが同一ファイル内に存在 Middle: 全てのクローンが同一ディレクトリ内に存在 Scattered: クローンはディレクトリをまたがって存在 同形コードフラグメントの数
Gemini – グループメトリクス NOF(g): グループ g に含まれるファイルの数 NOL(g): g に含まれるファイルの行数の和 NOT(g): g に含まれるファイルのトークン数の和 NOC(g): g に含まれるコードクローンの数 ROC(g): g がどの程度クローンになっているかを表す 例1. g1 にはファイル f1 と f2 が含まれている f1 は 200 トークンから成り,そのうちの 20 トークンがクローンと成っている f2 は 300 トークンから成り,そのうちの 80 トークンがクローンと成っている ROC(g1) = (20 + 80) / (200 + 300) ×100% = 20%
Aries - 概要(1/2) 目的 コードクローンの集約(リファクタリング)支援 リファクタリング支援のための処理 理由 CCFinder はコードクローンとトークンの列として検出するために,検出されたコードクローンはリファクタリングに適しているとは言えない 処理内容 CCFinder の検出したクローンから,まとまりのある部分を抽出 抽出部分を,メトリクスを用いて定量的な特徴づけ
Aries - 概要(2/2) 一般的なリファクタリングプロセス リファクタリング箇所の特定 リファクタリング方法の選定 リファクタリング効果の調査 ソースコードの修正 回帰テストの実施 Aries では 1. と 2. をサポート
Aries - リファクタリング箇所の抽出 目的 CCFinder はトークンの列としてコードクローンを検出 検出された全てのコードクローンがリファクタリング可能ではない リファクタリングのできそうな部分を抽出することで,より効率的なリファクタリング支援ができる 抽出箇所 プログラミング言語での構造的なまとまり 例: Java 言語 宣言単位 : クラス宣言, インターフェース宣言 メソッド単位 : メソッド本体,コンストラクタ, 文単位 : do文,for文,if文,switch文,synchronized文,try 文,while文
CCFinder が検出するコードクローン fragment 1 609: reset(); 610: grammar = g; 611: // Lookup make-switch threshold in the grammar generic options 612: if (grammar.hasOption("codeGenMakeSwitchThreshold")) { 613: try { 614: makeSwitchThreshold = grammar.getIntegerOption("codeGenMakeSwitchThreshold"); 615: //System.out.println("setting codeGenMakeSwitchThreshold to " + makeSwitchThreshold); 616: } catch (NumberFormatException e) { 617: tool.error( 618: "option 'codeGenMakeSwitchThreshold' must be an integer", 619: grammar.getClassName(), 620: grammar.getOption("codeGenMakeSwitchThreshold").getLine() 621: ); 622: } 623: } 624: 625: // Lookup bitset-test threshold in the grammar generic options 626: if (grammar.hasOption("codeGenBitsetTestThreshold")) { 627: try { 628: bitsetTestThreshold = grammar.getIntegerOption("codeGenBitsetTestThreshold"); Aries が抽出する部分 CCFinder が検出するコードクローン fragment 2 623: } 624: 625: // Lookup bitset-test threshold in the grammar generic options 626: if (grammar.hasOption("codeGenBitsetTestThreshold")) { 627: try { 628: bitsetTestThreshold = grammar.getIntegerOption("codeGenBitsetTestThreshold"); 629: //System.out.println("setting codeGenBitsetTestThreshold to " + bitsetTestThreshold); 630: } catch (NumberFormatException e) { 631: tool.error( 632: "option 'codeGenBitsetTestThreshold' must be an integer", 633: grammar.getClassName(), 634: grammar.getOption("codeGenBitsetTestThreshold").getLine() 635: ); 636: } 637: } 638: 639: // Lookup debug code-gen in the grammar generic options 640: if (grammar.hasOption("codeGenDebug")) { 641: Token t = grammar.getOption("codeGenDebug"); 642: if (t.getText().equals("true")) { This is an example of the extraction process. For these fragments, CCFinder detects the highlighted parts as clone pair. In this case, the proposed method extracts these “if statements” because these are the biggest blocks in the clones.
CCFinder が検出するコードクローン fragment 3 1007: if ( inputState.guessing==0 ) { 1008: buf.append(a.getText()); 1009: } 1010: { 1011: _loop144: 1012: do { 1013: if ((LA(1)==WILDCARD)) { 1014: match(WILDCARD); 1015: a=id(); 1016: if ( inputState.guessing==0 ) { 1017: buf.append('.'); buf.append(a.getText()); 1018: } 1019: } CCFinder が検出するコードクローン fragment 4 1527: if ( inputState.guessing==0 ) { 1528: t=a.getText(); 1529: } 1530: { 1531: _loop84: 1532: do { 1533: if ((LA(1)==COMMA)) { 1534: match(COMMA); 1535: id(); 1536: if ( inputState.guessing==0 ) { 1537: t+=","+b.getText(); 1538: } 1539: } This is another example. For this source code, CCFinder detects the highlighted parts as a clone pair. But this clone pair includes no structural block, so the proposed method does nothing.
Aries - メトリクスを用いた特徴づけ(1/2) 目的 抽出した部分はメソッドなどのまとまりのあるクローンになっているが,それらがどのようにリファクタリング可能であるかは不明 人が各抽出部分がどのように集約できるかを調査するのは非効率 メトリクスを用いて特徴づけを行い,どのように集約できるかを予測 どのように集約できるか 既存のリファクタリングパターンを用いる
Aries - メトリクスを用いた特徴づけ(2/2) 以下のリファクタリングパターン[1][2]が,コードクローンの集約に用いることができる Extract Class, Extract Method, Extract Super Class, Form Template Method, Move Method, Parameterize Method, Pull Up Constructor, Pull Up Method, Aries は抽出した各クローンに対して,上記のリファクタリングパターンのどれが適しているかを示す [1]: M. Fowler: Refactoring: Improving the Design of Existing Code, Addison-Wesley, 1999. [2]: http://www.refactoring.com/, 2004.
Aries - ボリュームメトリクス(LEN, POP, DFL) メトリクスグラフで用いているメトリクス LEN(S) : S に含まれるコード片のトークン数の平均値 POP(S) : S に含まれるコード片の数 DFL(S) : S に含まれる全てのコード片を1つのサブルーチンにマージした場合のトークン減少数の予測値 new sub routine caller statements
Aries - 結合度メトリクス(NRV, NSV) int a , b, c; … if( … ){ …; … = b + c; a = …; } assignment reference コード片 f1 例: ・クローンセット S1 は f1 と f2 を含んでいる ・コード片 f1 では, 外部定義の変数 b と c に対して参照, a に対して代入を行っている ・コード片 f2 では f1 と同様の処理が行われているとする then,NRV(S) = ( 2 + 2 ) / 2 = 2 NSV(S) = ( 1 + 1 ) / 2 = 1 NRV(S): S に含まれるコード片が,その外部で定義された変数をどの程度参照しているかを表す NSV(S): S に含まれるコード片が,その外部で定義された変数に対してどの程度代入を行っているかを表す 定義 クローンセット S はコード片 f1, f2, ・・・, fn を含んでいる si はコード片 fi の内部で参照されている外部定義変数の数を表す ti はコード片 fi の内部で代入されている外部定義変数の数を表す
Aries - 分散度メトリクス(DCH) DCH(S): S に含まれるコード片がクラス階層において,どの程度広く分散しているかを表す クラス B クラス C コード片 f1 コード片 f2 例 2: ・クローンセット S はコード片 f1 ,f2を含んでいる ・f1 を含むクラスと,f2を含むクラスの直接の親クラスが共通であった場合, DCH(S) = 1 コード片 f1 コード片 f2 class A class B 例 3: ・クローンセット S はコード片 f1 , f2 を含んでいる ・ コード片f1 を含むクラスと コード片f2 を含むクラスが共通の親クラスを持っていない場合, DCH(S) = ∞ 例 1: ・クローンセット S はコード片 f1 ,f2を含んでいる ・コード片f1 を含むクラスとコード片f2を含むクラスが同一であった場合, DCH(S) = 0 クラス A コード片 f1 コード片 f2 DCH(S): S に含まれるコード片がクラス階層において,どの程度広く分散しているかを表す 定義 クローンセット S はコード片 f1, f2, ・・・,fn を含む コード片 fi はクラス Ci 中に存在する クラス Cp は,クラス C1, C2, ・・・,Cn の共通の親クラスのうち,クラス階層的に最も下に位置するクラスを表す D(Ci, Cj) はクラス Ci と クラス Cj のクラス階層内での距離を現す もしクラス C1,C2,・・・,Cn が共通の親クラスを持たない時は DCH(S) は ∞ とする このメトリクスは対象ソフトウェアのクラス階層内のみで計算される
Aries - 分析手順(デモンストレーション) クローンセットの絞込み メトリクスグラフ メトリクス値による絞込み ユニットセレクションチェックボックス 単位による絞込み クローンセットの選択 クローンセットリスト 各メトリクス値に基づいたソーティングが可能 選択したクローンセット情報の取得 クローンセットビューワー コード片のリスト ソースコード コードクローン内で用いている外部定義変数一覧
Aries - 変数の種類 this_class_field 自クラスのフィールド super_class_field 親クラスのフィールド Interface_field インターフェースのフィールド local ローカル変数 outer 外側のクラス内で定義された変数
Libra - 概要 目的 ソースコード修正(デバッグ,機能追加など)の支援 あるコードクローンに対して修正を行うと,それと対応する全てのコードクローンに対して,同様の修正の是非を考慮する必要がある Gemini コンポーネントとの違い ユーザは検出したいコード片を入力 無駄なコードクローンは検出しない
Libra - 検出手法 CCFinder の検出オプションを利用 ファイル内クローン ファイル間クローン グループ間クローン ユーザが入力したコード片をグループ1,対象ソースコードをグループ2として,グループ間クローンのみを検出するオプションをつけて,CCFinder を実行する この検出手法のメリット 検出時間の短縮 CCFinder はコードクローンを検出した後,ソーティングを行ってからコードクローンを出力する 無駄なクローンを検出しないことで,ソーティング時間を短縮できる
Libra - 分析手順(デモンストレーション) コードクローン検出対象ファイルを選択 検出を行うコード片を入力 ディレクトリツリーで,どのファイルにクローンが含まれているのかを確認 ソースコードビューで,ファイルのどの部分がクローンになっているのかを確認
日立システムアンドサービス での適用事例 肥後 芳樹† ,前田憲一‡ †大阪大学 大学院情報科学研究科 y-higo@ist.osaka-u.ac.jp ‡株式会社 日立システムアンドサービス ke-maeda@hitachi-system.co.jp
適用対象 対象ソフトウェア 日立システムアンドサービス(社内システム) 規模 行数: 約7万行 クラス数: 309
コードクローン検出 検出オプション 最小一致トークン数: 50(約10行) 以下のユーザ定義名の正規化は行わない 変数の型 リテラル メソッド名 検出クローンセット数 宣言単位: 4 メソッド単位: 13 文単位: 49
用いたリファクタリングパターン 宣言,メソッド,文単位のクローンセットに対して以下のリファクタリングパターンの適用を試みた. 宣言単位: Extract Super Class クローンとなっているクラスに対して共通の親クラスを作成 共通の機能は親クラスに引き上げ クラスがまったく同一の場合は,1つを残して他は削除 メソッド単位: Move Method ユーティリティクラス等に移動 文単位: Extract Method メソッド内の一部分を新たなメソッドとして抽出
パターン1: Extract Super Class
パターン2: Move Method
パターン3: Extract Method void printOwing() { printBanner(); //print details System.out.println ("name: " + _name); System.out.println ("amount " + getOutstanding()); } printBanner(); printDetails(getOutstanding()); } void printDetails (double outstanding) { System.out.println ("amount " + outstanding);
絞込み条件 Extract Super Class クラス単位のクローンセット 親クラスのメンバを用いていない(NSV,NRV が0) 共通の親クラスがない(DCH が∞) Move Method メソッド本体のクローンセット 現在定義されているクラスのメンバを用いていない(NSV,NRVが0) Extract Method 文単位のクローンセット クローン外部で定義された変数に対しては,高々1つにしか代入を行っていない(NSV が1以下) 全てのクローンが単一のクラス内に存在する(DCHが0) 3つ以上同一のコード片が存在(POPが3以上)
絞込み結果 検出クローンセット数 宣言単位: 4 メソッド単位: 13 文単位: 49 絞込み条件に一致したクローンセット数 Extract super Class: 4 Move Method: 5 Extract Method: 12
評価方法(1/3) 絞り込まれた各クローンセットに対して以下の評価を行って頂いた クローンの現在の状態について( (a),(b)から選択) ソフトウェアのサイズ(※1) ソフトウェアのデザイン(※2) クラスの凝集度(※3) クラス間の結合度(※4) パフォーマンス (a)悪化させている, (b) 影響していない ※1 モジュール(クラスやメソッドなど)のステップ数やトークン数を表す ※2 クラスの階層構造やカプセル化を表す ※3 1つのクラスが責任を持って1つの機能を提供しているかを表す.1つのクラスが複数の事柄を扱っていたり,また特に何も機能を提供していない場合は,凝集度的にみて悪い(低い)ことを表す ※4 他のクラスのフィールドやメソッドを用いていることを表す.フィールドやメソッドが適切なクラスに定義されていないと必要以上に他クラスのフィールドやメソッドを呼び出すことになり,結合度的にみて悪い(高い)ことを表す.
評価方法(2/3) 評価の続き リファクタリングの効果について( (a) (b) (c) (d) から選択) サイズ面 デザイン面 クラスの凝集度の面 クラス間の結合度の面 パフォーマンス面 ソースコードの可読性 ソースコードの再利用性 (a) 改善される,(b) 将来的な問題の予防にはなる, (c) 効果なし,(d) 悪化する
評価方法(3/3) 評価の続き リファクタリングのコストについて((a) (b) (c) から選択) ソースコードの修正 回帰テスト 総合的な評価((a) (b) (c) (d) から選択) このクローンセットは (a) すぐにリファクタリングすべき (b) 将来的にはリファクタリングすべき (c) リファクタリングしてもしなくてもよい (d) リファクタリングすべきではない
評価結果 - Extract Super Class(1/3) (クラス単位の)クローンの現在の状態 全てのクローンセットがサイズとデザインを悪化させていた どのクローンセットも凝集度,結合度,パフォーマンスには影響していなかった (a) 悪化させている (b) 影響していない ソフトウェアのサイズついて 4 ソフトウェアのデザインについて クラスの凝集度について クラス間の結合度について パフォーマンスについて
評価結果 - Extract Super Class(2/3) (クラス単位の)リファクタリングの効果 全てのクローンセットのリファクタリングがサイズ,デザイン,可読性,再利用性の面で有効である どのクローンセットのリファクタリングも凝集度,結合度,パフォーマンス面では効果がない (a) 改善される (b) 将来的な問題の予防にはなる (c) 効果なし (d) 悪化する サイズ 2 デザイン クラスの凝集度 4 クラス間の結合度 パフォーマンス ソースコードの可読性 1 3 ソースコードの再利用性
評価結果 - Extract Super Class(3/3) (クラス単位の)リファクタリングのコスト ソースコードの修正はそれほどコストがかからない 回帰テストは煩雑になる場合がある (クラス単位の)総合的な評価 全てのクローンセットがリファクタリングするべき (a) 非常に煩雑である (b) 少し手間がかかる (c) すぐに完了できる ソースコードの修正は 3 1 回帰テストは 2 (a) すぐにすべき (b) 将来的にはすべき (c) してもしなくてもよい (d) すべきではない リファクタリングを 2
評価結果 - Move Method(1/3) (メソッド単位の)クローンの現在の状態 全てのクローンセットがサイズ,デザインを悪化させていた. 多くのクローンセットが凝集度を悪化させていた リファクタリングすべき箇所を検出できている ほとんどのクローンセットが結合度,パフォーマンスには影響していなかった (a) 悪化させている (b) 影響していない ソフトウェアのサイズついて 5 ソフトウェアのデザインについて クラスの凝集度について 4 1 クラス間の結合度について パフォーマンスについて
評価結果 - Move Method(2/3) (メソッド単位の)リファクタリングの効果 全てのクローンセットのリファクタリングがサイズ,デザイン,凝集度,可読性,再利用性の面で有効である どのクローンセットのリファクタリングも結合度,パフォーマンス面では効果がない (a) 改善される (b) 将来的な問題の予防にはなる (c) 効果なし (d) 悪化する サイズ 5 デザイン クラスの凝集度 クラス間の結合度 パフォーマンス ソースコードの可読性 4 1 ソースコードの再利用性
評価結果 - Move Method(3/3) (メソッド単位の)リファクタリングのコスト ソースコードの修正,回帰テスト共にそれほどコストがかからない (メソッド単位の)総合的な評価 全てのクローンセットがリファクタリングするべき (a) 非常に煩雑である (b) 少し手間がかかる (c) すぐに完了できる ソースコードの修正は 5 回帰テストは (a) すぐにすべき (b) 将来的にはすべき (c ) してもしなくてもよい (d) すべきではない リファクタリングを 4 1
評価結果 ー Extract Method(1/3) (文単位の)クローンの現在の状態 一部のクローンセットがサイズ,デザインを悪化させていた. 全てのクローンセットが凝集度,結合度,パフォーマンスには影響していなかった (a) 悪化させている (b) 影響していない ソフトウェアのサイズついて 2 10 ソフトウェアのデザインについて クラスの凝集度について 12 クラス間の結合度について パフォーマンスについて
評価結果 - (2/3) (文単位の)リファクタリングの効果 サイズ,デザイン,可読性,再利用性は,リファクタリングの効果あり,効果なし,逆効果に分かれた 全てのリファクタリングが凝集度,結合度,パフォーマンスについては効果がなかった (a) 改善される (b) 将来的な問題の予防にはなる (c) 効果なし (d) 悪化する サイズ 5 4 3 デザイン 6 クラスの凝集度 12 クラス間の結合度 パフォーマンス ソースコードの可読性 ソースコードの再利用性 2
評価結果 - (3/3) (文単位の)リファクタリングのコスト 半数のクローンセットが,ソースコード修正・回帰テスト共にコストが大きい (文単位の)総合的な評価 半数のクローンセットがリファクタリングすべきでない (a) 非常に煩雑である (b) 少し手間がかかる (c) すぐに完了できる ソースコードの修正は 4 5 3 回帰テストは 6 2 (a) すぐにすべき (b) 将来的にはすべき (c) してもしなくてもよい (d) すべきではない リファクタリングを 3 6
考察 クラス単位,メソッド単位のクローンセットはリファクタリングすべき 対象のクローンセットはソフトウェアの保守性を悪化させていた リファクタリングにより保守性の改善を期待できる コストがそれほどかからない 文単位のクローンセットはリファクタリングすべきでないものが存在した メソッドの一部分を抽出する作業は煩雑 条件をきつくする 修正支援を行う 用いているフレームワークに依存したクローンセット
CCFinder: Clone Detection Process Source files Lexical analysis Transformation Token sequence Match detection Transformed token sequence Clones on transformed sequence Formatting Clone pairs 1. static void foo() throws RESyntaxException { 2. String a[] = new String [] { "123,400", "abc", "orange 100" }; 3. org.apache.regexp.RE pat = new org.apache.regexp.RE("[0-9,]+"); 4. int sum = 0; 5. for (int i = 0; i < a.length; ++i) 6. if (pat.match(a[i])) 7. sum += Sample.parseNumber(pat.getParen(0)); 8. System.out.println("sum = " + sum); 9. } 10. static void goo(String [] a) throws RESyntaxException { 11. RE exp = new RE("[0-9,]+"); 12. int sum = 0; 13. for (int i = 0; i < a.length; ++i) 14. if (exp.match(a[i])) 15. sum += parseNumber(exp.getParen(0)); 16. System.out.println("sum = " + sum); 17. } Lexical analysis Transformation Token sequence Match detection Transformed token sequence Clones on transformed sequence Formatting Lexical analysis Transformation Token sequence Match detection Transformed token sequence Clones on transformed sequence Formatting Lexical analysis Transformation Token sequence Match detection Transformed token sequence Clones on transformed sequence Formatting 1. static void foo() throws RESyntaxException { 2. String a[] = new String [] { "123,400", "abc", "orange 100" }; 3. org.apache.regexp.RE pat = new org.apache.regexp.RE("[0-9,]+"); 4. int sum = 0; 5. for (int i = 0; i < a.length; ++i) 6. if (pat.match(a[i])) 7. sum += Sample.parseNumber(pat.getParen(0)); 8. System.out.println("sum = " + sum); 9. } 10. static void goo(String [] a) throws RESyntaxException { 11. RE exp = new RE("[0-9,]+"); 12. int sum = 0; 13. for (int i = 0; i < a.length; ++i) 14. if (exp.match(a[i])) 15. sum += parseNumber(exp.getParen(0)); 16. System.out.println("sum = " + sum); 17. } Next, I explain the clone detection process. In this explanation, I use this source code as an example. And this is the model of the detection process. Lexical analysis is done first of all, and the source code is divided into tokens like this. Next process is transformation. In this process, replacement of identifiers is performed, and this token sequence is generated. Next process is Match detection. CCFinder detects clone pairs from the token sequence which is generated in previous step At last, formatting is performed to make clone pairs map on actual source code. CCFinder outputs the portion information of clone pairs by performing this process.
Aries - その他のメトリクス1(DOC) DOC(S): そのクローンセットに含まれるコード片がどの程度異なるクラスに散らばっているか表す 例: クローンセット S は10個のコード片を含んでいるとする 全てのコード片が異なるクラスにある場合, DOC(S) = ( 10 – 1) / (10 – 1) = 1 5つのクラスにコード片がある場合, DOC(S) = (5 – 1) / (10 – 1) = 0.44 2つのクラスにコード片がある場合, DOC(S) = (2 – 1) / (10 -1) = 0.11 全てのコード片が同一のクラスにある場合, DOC(S) = (1 -1) / (10 -1) = 0 POPの値が大きいクローンセットに対して有効 全てのコード片を1つにまとめるのは難しい
Aries - その他のメトリクス2(SMT) SMT(S): そのクローンセットに含まれる(メソッド単位の)コード片の返り値,引数の型がどの程度一致するかを表す 現在の”一致”の定義 プリミティブ型の場合, まったく同一である時 オブジェクト型の場合, 同一の型もしくは,異なっていても共通の親クラスをもつオブジェクト型の時 例: メソッドAとメソッドBはクローンになっており(クローンセット S) ,それぞれ引数の数は4つである. 全ての引数と返り値の型が一致する場合 SMT(S) = (4 + 1) / (4 + 1) = 1 二つの引数の型が一致する場合 SMT(S) = 2 / (4 + 1) = 0.4 全ての引数と返り値の型が一致しない場合 SMT(S) = 0 / (4 + 1) = 0
Aries - その他のメトリクス3(KMC) KMC(S): そのクローンセットに含まれるコード片が存在するメソッドが,他のメソッドとどの程度結合しているかを表す 結合は以下の三種類で計る メソッド呼び出しの引数 メソッド呼び出しの返り値 メソッド間の共有変数 KMC(S)の値が大きい メソッド間の結合度が大きい リファクタリングすべき?