90 likes | 161 Views
水平結合がない例 CH.ifa.draw.util.GraphLayout.relax. edgeEnum を参照も代入もしおらず水平結合がない. 000 public synchronized void relax() { ・・・・・・・・ 003 Enumeration edgeEnum = edges.keys(); 004 while (edgeEnum.hasMoreElements()) { ・・・・・・・・ 028 } 029
E N D
水平結合がない例CH.ifa.draw.util.GraphLayout.relax edgeEnumを参照も代入もしおらず水平結合がない 000 public synchronized void relax() { ・・・・・・・・ 003 Enumeration edgeEnum = edges.keys(); 004 while (edgeEnum.hasMoreElements()) { ・・・・・・・・ 028 } 029 030 Enumeration nodeEnum1 = nodes.elements(); 031 while (nodeEnum1.hasMoreElements()) { ・・・・・・・・ 078 } 079 080 Enumeration nodeEnum = nodes.keys(); 081 while (nodeEnum.hasMoreElements()) { ・・・・・・・・ 101 } 102 } edgeEnum,nodeEnumを参照も代入もしおらず水平結合がない While1 While2 While3 While1,2,3を別々のメソッドとして抽出すれば While2ではedgeEnumが不可視に While3ではedgeEnum,nodeEnum1が不可視になる
水平結合がない例2org.apache.tools.ant.taskdefs.JavaDoc(apache-ant-1.6.5)水平結合がない例2org.apache.tools.ant.taskdefs.JavaDoc(apache-ant-1.6.5) If1とwhile1は変数による結合が0なので、容易に別々のメソッドとして抽出可能 00 private void parsePackages(Vector pn, Path sp) { 01 Vector addedPackages = new Vector(); 02 Vector dirSets = (Vector) packageSets.clone(); ・・・・・・・・・ 08 if (sourcePath != null && packageNames.size() > 0) { ・・・・・・・・ 32 for (int i = 0; i < pathElements.length; i++) { ・・・・・・・・ 37 dirSets.addElement(ds); 38 } 39 } 40 41 Enumeration e = dirSets.elements(); 42 while (e.hasMoreElements()) { ・・・・・・・・ 49 for (int i = 0; i < dirs.length; i++) { ・・・・・・・・ 60 if (files.length > 0) { ・・・・・・・・ 64 if (!addedPackages.contains(packageName)) { 65 addedPackages.addElement(packageName); 66 pn.addElement(packageName); 67 } 68 } 69 } 70 if (containsPackages) { ・・・・・・・・ 73 sp.createPathElement().setLocation(baseDir); 74 } else { ・・・・・・・ 77 } 78 } 79 } if1 If1を新規メソッドとして抽出すれば If1で参照していないローカル変数pn、sp、addedPackages は不可視になる その際dirSetsの宣言をif1を抽出したメソッド内で行えばwhile1においてdirSetsを不可視にできる While1
水平結合がある例org.antlr.tool.Attribute(antlr-3.0.1)水平結合がある例org.antlr.tool.Attribute(antlr-3.0.1) 00 protected void extractAttribute(String decl) { ・・・・・・・・ 04 boolean inID = false; 05 int start = -1; 06 int rightEdgeOfDeclarator = decl.length()-1; 07 int equalsIndex = decl.indexOf('='); 08 if ( equalsIndex>0 ) { ・・・・・・・・ 11 rightEdgeOfDeclarator = equalsIndex-1; 12 } ・・・・・・・・ 14 for (int i=rightEdgeOfDeclarator; i>=0; i--) { 15 if ( !inID && Character.isLetterOrDigit(decl.charAt(i)) ) { 16 inID = true; 17 } else if ( inID && !(Character.isLetterOr ・・・・・・・・ ・・・・・・・・ 22 start = i+1; 23 break; 24 } 25 } 26 if ( start<0 && inID ) { 27 ・・・・・・・・ 28 } ・・・・・・・・ 34 for (int i=start; i<=rightEdgeOfDeclarator; i++) { ・・・・・・・・ 42 if ( i==rightEdgeOfDeclarator ) { 43 stop = i+1; 44 } 45 } ・・・・・・・・ 60 } (水平結合1の例) rihtEdgeOfDeclaratorはif1で代入されFor1,for2で参照されているが 他のif1の外部で宣言された変数に対する代入は行われていないので rihtEdgeOfDeclaratorを返り値として返せばメソッドとして抽出可能 if1 for1 if2 (平結合2以上の例) for1ではstartとinldに代入しており,if2ではその両方を参照しているため for1のみをメソッド抽出する場合、 startとinld両方の値を格納したデータ構造を作成しなければならない For1とif2を1つのメソッドとして抽出した場合、 For2(if2以降)ではinldは参照されていないので startのみを返り値として返すだけでメソッド抽出可能 for2
実験環境とかかった時間 • CPU3.00GHZ • メモリ 1.99 GB RAM • かかった時間 • antlr-3.0.1 • ローカル変数参照状況解析4.112 sec • MASU解析 15.907 sec • トータル 20.019 sec • Jhotdraw54b2 • ローカル変数参照状況解析 3.735 sec • MASU解析 13.188 sec • トータル 16.860 sec • apache-ant-1.6.5 • ローカル変数参照状況解析 12.563 sec • MASU解析 47.812 sec • トータル 60.375 sec
実験対象 • antlr-3.0.1 • 44036 loc • 142 files • Jhotdraw54b2 • 40986 loc • 289 files • apache-ant-1.6.5 • 166295 loc • 674 files ↑locは空白行・コメントもカウント
00 private void parsePackages(Vector pn, Path sp) { 01 Vector addedPackages = new Vector(); 02 Vector dirSets = (Vector) packageSets.clone(); ・・・・・・・・・ 08 if (sourcePath != null && packageNames.size() > 0) { ・・・・・・・・ 32 for (int i = 0; i < pathElements.length; i++) { ・・・・・・・・ 37 dirSets.addElement(ds); 38 } 39 } 40 41 Enumeration e = dirSets.elements(); 42 while (e.hasMoreElements()) { ・・・・・・・・ 78 } 79 } Future work if1 • 現在の問題点 • 現在の実装では抽出すべきブロック文と関連のある周囲の文を特定できない • リファクタリング適用範囲(ブロック文とそのブロック文に関連する周囲の文)を特定するためのスライシング技術の提案と実装 • 参照(代入)されているローカル変数のうちブロック文の外部で宣言されている変数を基点にスライシングを行う(if1の場合ならdirSetsを基点にスライシングを行う • リファクタリングによりローカル変数の隠蔽度があがることを考慮したスライシング • 例えばif1をリファクタリングする場合、リファクタリング範囲を02~39とした場合、while1内でdirSetsは参照していないが不可視になっていない。 • リファクタリング範囲を02~41にした場合dirSetsは新規メソッド内に含まれwhile1からは不可視になる While1 00 private void parsePackages(Vector pn, Path sp) { 01 Vector addedPackages = new Vector(); 02 Vector dirSets = getDirSets(); 40 41 Enumeration e = dirSets.elements(); 42 while (e.hasMoreElements()) { ・・・・・・・・ 78 } 79 } While1 00 private void parsePackages(Vector pn, Path sp) { 01 Vector addedPackages = new Vector(); 40 41 Enumeration e = getDirSetsEnumeration(); 42 while (e.hasMoreElements()) { ・・・・・・・・ 78 } 79 } While1
Future work • 新規提案メトリクス(Number of Available Variables)とバグとの関連や他のメトリクスとの相関を定量的に評価 • 提案しているリファクタリング手法の妥当性の評価 • 定量的な評価は対象とするソフトウェアの質に大きく依存するので、定量的な評価を行うよりも開発者(できれば作成者)の生の意見を聞いて妥当性を評価する
関連研究 • 基本ブロックスライシングを用いたメソッド抽出リファクタリング • メソッド抽出リファクタリングを半自動化 • ユーザーが行う作業 • プログラムにおいて着目する変数を指定 • ツールにより生成された候補から適切なメソッドを選択 • 選択したメソッドに名前をつける • メソッド抽出範囲の特定手法(基本ブロックスライシング)を提案 • 従来のスライシングアルゴリズムに対して、データフローおよび制御フローに基づく領域(region)という概念を導入し、この領域によりメソッドの抽出対象コードを分割