370 likes | 444 Views
差分を含む類似メソッドの集約支援ツール. ○ 後藤 祥 1 , 吉田 則裕 2 , 井岡 正和 1 , 井上 克郎 1 1 大阪大学 2 奈良先端科学技術大学院大学. 研究概要. 類似メソッドの集約作業を支援する手法を提案 抽象構文木を用いて集約候補を検出 集約候補を凝集度の高い順に並び替えて提示 手法を統合開発環境のプラグインとして実装 オープンソース上の類似メソッドに手法を適用 アンケートによって集約候補を評価 有用 な集約候補を提示できていることを確認. 類似メソッド. 互いに一致または 類似したメソッド ソフトウェア の保守性を低下 させる要因.
E N D
差分を含む類似メソッドの集約支援ツール ○ 後藤 祥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.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); } public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer.array(),0); } ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・
類似メソッド集約手順(2/3) • メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); } public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer.array(),0); } 条件1:全てのコード片はメソッド抽出可能である ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・
類似メソッド集約手順(2/3) • メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); } public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer.array(),0); } 条件2:同じメソッド内の各コード片は重複部分がない ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・
類似メソッド集約手順(2/3) • メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); } public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer.array(),0); } 条件3:各差分はいずれかのコード片に含まれる ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・
類似メソッド集約手順(2/3) • メソッドとして抽出するコード片の集合を決定する public void similarMethodA(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); } public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer.array(),0); } 条件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.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); } public void similarMethodB(){ if(finalBuffer.remaining() < 8){ while(finalBuffer.remaining() > 0){ finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer.array(),0); } ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ 12
提案手法の概要 提案するツール 差分を含む類似メソッド 1. 類似メソッド間の差分を特定する 開発者 差分となっているコード片の集合 入力 2. 集約候補を検出する 出力 類似メソッド対の集約候補 1 2 3 ・・・ 3. 凝集度を用いて集約候補を並び替える 順位づけされた 集約候補一覧
1.類似メソッド間の差分の特定 差分を含む類似メソッド 1-1抽象構文木の比較 1-2差分を含む文の特定 1. 類似メソッド間の差分を特定する 差分となっているコード片の集合 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 = max = b > max b
1-1 ASTの比較 • 根ノードから葉に向かって再帰的に比較 • ノードのラベルを比較して異なっていれば差分とする Block Block 比較 Expression Statement Expression Statement Expression Statement Expression Statement a b a b = b = c = b = d 差分となっているノード
1-2 差分を含む文の特定 • AST上の差分であるノードから親ノードへ辿っていく • 最初に到達した特殊ノードを根とする部分木が差分となっている文に対応している Block Block Expression Statement Expression Statement Expression Statement Expression Statement = = a b b d = a b b c = a = b; b = c; a = b; b = d; ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・
1-3 差分の統合 • これまでの操作で差分となっている文が検出される • 隣接している差分の統合を行う • 最終的に検出される差分の数を減らすため 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); finalBuffer.position(0); } 拡大 メソッド抽出可能か判定
2-2 集約候補の検出 差分δ1を含みメソッド抽出可能な コード片の集合 if(finalBuffer.remaining() < 8) { while(finalBuffer.remaining() > 0) { finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); 差分δ1 条件を満たすように コード片を選択する 差分δ2 差分δ2を含みメソッド抽出可能な コード片の集合
2-3 集約候補のフィルタリング • 広い範囲をメソッドとして抽出する集約候補は除外 • 抽出後のメソッドが再び類似メソッドとなる if(finalBuffer.remaining() < 8) { while(finalBuffer.remaining() > 0) { finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer); if(finalBuffer.remaining() < 8) { while(finalBuffer.remaining() > 0) { finalBuffer.put((byte)0); } finalBuffer.position(0); transform(finalBuffer.array(),0); finalBuffer.position(0); } finalBuffer.putLong(length << 3); finalBuffer.position(0); transform(finalBuffer.array(),0); 抽出範囲が広すぎる
3. 集約候補の並び替え • 開発者は提示された候補の中から1つを選択する • 集約候補の数が多い場合,選択する作業が困難になる • 凝集度が高い集約候補から順番に提示する • 凝集度の高いメソッドは保守性や可読性に優れている 1 2 3 4 ・・・ 凝集度が高い良い候補
凝集度メトリクス • プログラムスライスを用いた凝集度メトリクス [1] • メソッドの返り値に着目して凝集度を計測 • 提案手法ではメソッドの引数も使用する • 返り値が存在しないメソッドの凝集度を計算するため • 3種類のメトリクスを使用 • FTightness, FCoverage, FOverlap • それぞれ独立に使用して3つのランキングを生成する [1] Weiser, M.: Program slicing, Proc. of ICSE1981, pp.439–449 (1981)
凝集度の計算例 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 全スライスの積集合 引数 a,b を起点とした 前向きスライス SLa SLb SLint | | | | | | | | | | SLresult | | | | | | 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.
平均適合率の計算例 全体の候補数:2 選択された候補数:1 適合率 = 0.5 平均値を計算 全体の候補数:3 選択された候補数:2 適合率 = 0.667 平均適合率 = 0.532 全体の候補数:7 選択された候補数:3 適合率 = 0.429
結果(1/2) 全体の2割程度が被験者に選択されている 平均適合率の結果は Ant で約0.5,ANTLRで約0.4
結果(2/2) • Ant に対するFTightness の結果
まとめ • 類似メソッドの集約作業を支援する手法を提案 • ASTを用いた集約候補の検出 • 凝集度を用いた集約候補の並び替え • 実際の類似メソッドへの適用・アンケート評価 • 提示した候補のうち,2割程度が有用な候補であった
今後の課題 • 多数の類似メソッドを対象とした適用実験 • ツールへの機能追加 • 3つ以上の類似メソッドへの適用 • 集約候補の検索機能 • ツールを使用した評価 • 集約作業の支援にどの程度有効であるか