○ 後藤 祥1,吉田 則裕2 ,井岡 正和1 ,井上 克郎1 1大阪大学 2奈良先端科学技術大学院大学 差分を含む類似メソッドの集約支援ツール ○ 後藤 祥1,吉田 則裕2 ,井岡 正和1 ,井上 克郎1 1大阪大学 2奈良先端科学技術大学院大学
研究概要 類似メソッドの集約作業を支援する手法を提案 手法を統合開発環境のプラグインとして実装 オープンソース上の類似メソッドに手法を適用 抽象構文木を用いて集約候補を検出 集約候補を凝集度の高い順に並び替えて提示 手法を統合開発環境のプラグインとして実装 オープンソース上の類似メソッドに手法を適用 アンケートによって集約候補を評価 有用な集約候補を提示できていることを確認
類似メソッド 互いに一致または類似したメソッド ソフトウェアの保守性を低下させる要因 欠陥 同様の欠陥が存在する可能性が高い ・・・ 類似メソッド 同様の欠陥が存在する可能性が高い
類似メソッドの集約 類似メソッドをまとめて1つのメソッドにする 類似メソッドに差分が存在する場合 差分をメソッドとして抽出して,類似メソッドを完全一致させる 集約
類似メソッド集約手順(1/3) 類似メソッド間の差分を特定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.putLong(length << 3); ・ ・ ・ ・
条件1:全てのコード片はメソッド抽出可能である 類似メソッド集約手順(2/3) メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.putLong(length << 3); 条件1:全てのコード片はメソッド抽出可能である ・ ・ ・ ・
条件2:同じメソッド内の各コード片は重複部分がない 類似メソッド集約手順(2/3) メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.putLong(length << 3); 条件2:同じメソッド内の各コード片は重複部分がない ・ ・ ・ ・
条件3:各差分はいずれかのコード片に含まれる 類似メソッド集約手順(2/3) メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.putLong(length << 3); 条件3:各差分はいずれかのコード片に含まれる ・ ・ ・ ・
条件4:抽出後に類似メソッドが完全に一致する 類似メソッド集約手順(2/3) メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.putLong(length << 3); 条件4:抽出後に類似メソッドが完全に一致する ・ ・ ・ ・
類似メソッド集約手順(3/3) 各コード片をメソッドとして抽出する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } methodA(); methodB(); public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } methodA(); methodB(); ・ ・ ・ ・
集約作業における問題点 問題点1 : 条件を満たすコード片の集合を,どのように探すか 問題点2 : 条件を満たすコード片の集合のうち,どれを選択するか
提案手法 集約候補を提示することで集約作業を支援 凝集度を用いて集約候補の並び替えを行う 集約候補:メソッドとして抽出するコード片の集合 条件を満たすコード片の集合を提示する 凝集度を用いて集約候補の並び替えを行う 凝集度の高いメソッドは保守性や可読性に優れている 凝集度の高い集約候補を先に提示する
集約候補の例 メソッドとして抽出するコード片の集合 public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.putLong(length << 3); ・ ・ ・ ・ 12
提案手法の概要 開発者 差分を含む類似メソッド 順位づけされた 集約候補一覧 提案するツール 1. 類似メソッド間の差分を特定する 差分となっているコード片の集合 入力 2. 集約候補を検出する 出力 類似メソッド対の集約候補 1 2 3 ・・・ 3. 凝集度を用いて集約候補を並び替える 順位づけされた 集約候補一覧
1.類似メソッド間の差分の特定 差分を含む類似メソッド 1-1 抽象構文木の比較 1-2 差分を含む文の特定 1-1 抽象構文木の比較 1. 類似メソッド間の差分を特定する 1-2 差分を含む文の特定 差分となっているコード片の集合 1-3 差分の統合
抽象構文木(AST) 各ノードはラベルを持つ(タイプや値を表す) ASTにおける特殊ノードを定義する ソースコード中で1つの文を表すノード Method Declaration int max(int a, int b){ int max = a; if(b > max) max = b; return max; } VariableDeclaration Statement IfStatement ReturnStatement Infix Expression Expression Statement int max = a max 特殊ノード b > max max = b
1-1 ASTの比較 根ノードから葉に向かって再帰的に比較 ノードのラベルを比較して異なっていれば差分とする 比較 差分となっているノード Block Block 比較 Expression Statement Expression Statement Expression Statement Expression Statement a = b b = c a = b b = d 差分となっているノード
1-2 差分を含む文の特定 AST上の差分であるノードから親ノードへ辿っていく 最初に到達した特殊ノードを根とする部分木が差分となっている文に対応している Block Block Expression Statement Expression Statement Expression Statement Expression Statement a = b b = c a = b b = d a = b; b = c; a = b; b = d; ・ ・ ・ ・
1-3 差分の統合 1.隣接している差分の統合 2.中括弧で囲まれたブロック内の差分の統合 これまでの操作で差分となっている文が検出される 隣接している差分の統合を行う 最終的に検出される差分の数を減らすため 1.隣接している差分の統合 2.中括弧で囲まれたブロック内の差分の統合
2.集約候補の検出 差分となっているコード片の集合 2-1 メソッド抽出可能な コード片の検出 2-2 集約候補の検出 2.集約候補の検出 2-3 集約候補のフィルタリング 集約候補一覧
2-1 メソッド抽出可能なコード片の検出 拡大 メソッド抽出可能か判定 差分を含み抽出可能なコード片を全て検出する コード片の拡大とメソッド抽出可能であるかの判定を繰り返し行う if(finalBuffer.remaining() < 8) { while(finalBuffer.remaining() > 0) { finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); 拡大 メソッド抽出可能か判定
2-2 集約候補の検出 差分δ1 差分δ2 条件を満たすように コード片を選択する 差分δ1を含みメソッド抽出可能な コード片の集合 if(finalBuffer.remaining() < 8) { while(finalBuffer.remaining() > 0) { finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); 差分δ1 条件を満たすように コード片を選択する 差分δ2 差分δ2を含みメソッド抽出可能な コード片の集合
2-3 集約候補のフィルタリング 広い範囲をメソッドとして抽出する集約候補は除外 抽出後のメソッドが再び類似メソッドとなる if(finalBuffer.remaining() < 8) { while(finalBuffer.remaining() > 0) { finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.putLong(length << 3); if(finalBuffer.remaining() < 8) { while(finalBuffer.remaining() > 0) { finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.putLong(length << 3); 抽出範囲が広すぎる
3. 集約候補の並び替え 開発者は提示された候補の中から1つを選択する 凝集度が高い集約候補から順番に提示する 集約候補の数が多い場合,選択する作業が困難になる 凝集度が高い集約候補から順番に提示する 凝集度の高いメソッドは保守性や可読性に優れている 1 2 3 4 ・・・ 凝集度が高い良い候補
凝集度メトリクス プログラムスライスを用いた凝集度メトリクス [1] 提案手法ではメソッドの引数も使用する 3種類のメトリクスを使用 メソッドの返り値に着目して凝集度を計測 提案手法ではメソッドの引数も使用する 返り値が存在しないメソッドの凝集度を計算するため 3種類のメトリクスを使用 FTightness, FCoverage, FOverlap それぞれ独立に使用して3つのランキングを生成する [1] Weiser, M.: Program slicing, Proc. of ICSE1981, pp.439–449 (1981)
凝集度の計算例 FTightness = 0.500 FCoverage = 0.722 FOverlap = 0.750 積集合に含まれる文の数 メソッドの文の数 積集合に含まれる文の数 各スライスに含まれる文の数 各スライスに含まれる文の数 メソッドの文の数 全スライスの積集合 引数 a,b を起点とした 前向きスライス | SLa | SLb | SLresult | SLint int permutation(int a, int b) { int i; int result = 1; for (i = 0; i < b; i++) { result = result * a; a = a – 1; } return result; 1 2 3 4 5 6 7 8 9 FTightness = 0.500 FCoverage = 0.722 返り値 result を起点とした 後ろ向きスライス FOverlap = 0.750
実装 提案手法を Eclipse プラグインとして実装 集約候補の選択タブ(番号は検出順) 集約候補中のコード片 メトリクスの選択
適用実験 オープンソース上の類似メソッドに手法を適用 出力結果に対して評価アンケートを実施 アンケート内容 被験者が良いと思う候補を提示できているか 被験者は学生15名(ソフトウェア工学関連の研究室に所属) アンケート内容 出力された候補のうち,上位10候補を被験者へ提示 提示された候補のうち良いと思う候補を選択 (複数可)
実験対象 Ant プロジェクト executeDrawOperation メソッド Arc クラス, Ellipse クラス ANTLR プロジェクト genErrorHandler メソッド CppCodeGenerator クラス, JavaCodeGenerator クラス フィルタリングの閾値を0.5に設定して手法を適用 Ant :14 個の集約候補を検出(フィルタリング前は23個) ANTLR : 6 個の集約候補を検出(フィルタリング前は34個)
評価尺度(1/2) 平均候補選択率 平均候補選択率 = 全体のうち被験者に選択された集約候補の割合 選択された候補数 提示した全候補数 被験者にとって有用な集約候補がどれだけ存在するか 選択された候補数 平均候補選択率 = 提示した全候補数
評価尺度(2/2) 平均適合率 平均適合率 = 検索エンジンなどのランキングを評価する尺度 [2] 被験者に選択された集約候補が上位に提示されていたか 平均適合率 = :正解集合 : 中の 番目の要素がランキングに現れた時の適合率 [2] Baeza-Yates, R. et al. : Modern Information Retrieval, Addison Wesley, second edition,2011.
平均適合率の計算例 平均値を計算 平均適合率 = 0.532 順位 候補 選択 1 A 2 B ○ 3 C 4 D 5 E 6 F 7 G 8 H 9 I 10 J 全体の候補数:2 選択された候補数:1 適合率 = 0.5 平均値を計算 全体の候補数:3 選択された候補数:2 適合率 = 0.667 平均適合率 = 0.532 全体の候補数:7 選択された候補数:3 適合率 = 0.429
結果(1/2) 平均適合率の結果は 全体の2割程度が被験者に選択されている Ant で約0.5,ANTLR で約0.4 メトリクス 平均候補選択率 平均適合率 Ant FTightness 0.253 0.533 FCoverage 0.213 0.560 FOverlap 0.535 ANTLR 0.267 0.438 0.346 全体の2割程度が被験者に選択されている 平均適合率の結果は Ant で約0.5,ANTLR で約0.4
結果(2/2) Ant に対する FTightness の結果 1位 2位 3位 4位 5位 6位 7位 8位 9位 10位 被験者A ○ 被験者B 被験者C 被験者D 被験者E 被験者F 被験者G 被験者H 被験者I 被験者J 被験者K 被験者L 被験者M 被験者N 被験者O
まとめ 類似メソッドの集約作業を支援する手法を提案 実際の類似メソッドへの適用・アンケート評価 ASTを用いた集約候補の検出 凝集度を用いた集約候補の並び替え 実際の類似メソッドへの適用・アンケート評価 提示した候補のうち,2割程度が有用な候補であった
今後の課題 多数の類似メソッドを対象とした適用実験 ツールへの機能追加 ツールを使用した評価 3つ以上の類似メソッドへの適用 集約候補の検索機能 ツールを使用した評価 集約作業の支援にどの程度有効であるか