コードクローンを対象とした リファクタリングの有効性に関する調査 肥後 芳樹,楠本 真二,井上 克郎 大阪大学 大学院情報科学研究科 {y-higo, kusumoto, inoue}@ist.osaka-u.ac.jp Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 1 コードクローン コードクローンとは ソースコード中に存在する一致または類似したコード片 コピーアンドペーストなどのさまざまな理由により生成される コピーアンドペースト コピーアンド ペースト ソフトウェアの保守を困難にする あるコード片にバグがあると,そのコードクローン全てについて修 正の検討を行う必要がある 機能を追加する場合も同様のことがいえる Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 2 リファクタリング ソフトウェアの外部的な振る舞いを保ったままで,内部構造 を改善していくこと リファクタリングを行うことでソフトウェアの保守性が増し,後の 保守作業効率が向上する これまでに多くのリファクタリングすべきコードのパターン(Bad Smell)とその対処法(Refactoring Pattern)が提案されて いる[1][2] コードクローンは最も優先してリファクタリングを行うべきものの1 つとされている しかし,具体的な評価事例はほとんど報告されていない [1] M. Fowler: Refactoring: Improving the Design of Existing Code, Addison-Wesley, 1999. [2] Refactoring Home Page: http://www.refactoring.com/ Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 3 研究の目的と概要 コードクローンに対するリファクタリングのコストと効果を調査し, その有効性を評価する 実験対象は複数バージョンのソースコードが公開されている オープンソースソフトウェア 最初のバージョンに対してコードクローン検出 & リファクタリング リファクタリングのコストとして以下のものを用いた ソースコード修正と回帰テストに要した時間 リファクタリングの効果として以下のものを用いた CKメトリクス値の変化 総行数の増減 集約箇所の後のバージョンでの修正回数 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 4 CKメトリクス Chidamber と Kemerer が提案したオブジェクト指向ソフトウェアの複 雑度を測定するメトリクス[3]であり,広く用いられている 各メトリクスは,クラス単位で計測される WMC(Weighted Methods per Class): そのクラスに定義されて いるメソッドに重み付けをして足し合わせた値 DIT(Depth of Inheritance Tree): クラス階層における継承の深さ NOC(Number of Children): サブクラスの数 CBO(Coupling Between Objects): 関係しているクラスの数 RFC(Response For a Class): そのクラスのオブジェクトにメッセー ジが送られた結果,実行されうるすべてのメソッドの数を表す LCOM(Lack of Cohesion Of Methods): そのクラスのメソッドが お互いに関連している程度を表す [3] Chidamber S. R. and Kemerer C. F.: “A Metrics Suite for Object Oriented Design”, IEEE Transaction on Software Engineering, Vol. 20, No. 6, pp. 476-493 (1994). Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 5 コードクローンに対するリファクタリング コードクローンは最も優先してリファクタリングを行うべきコード (のパターン)の1つとされている どのようにリファクタリングを行うかは,コードクローン間の関係 によって異なる[1] 同一クラス内の複数個所が重複している場合 兄弟クラス間で重複コードが見られる場合 まったく関係のないクラス間で重複コードが見られる場合 [1] M. Fowler: Refactoring: Improving the Design of Existing Code, Addison-Wesley, 1999. Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 6 コードクローンに対するリファクタリング: 同一クラス内の複数箇所が重複している場合 Extract Method を適用して,各メソッドの内部からコードク ローンを抽出し,新たなメソッドとして定義する. 抽出箇所は,そのメソッドの呼び出し文に変更する. void printTaxi(int amount){ String name = getTaxiName(); System.out.println(“name:” + name); System.out.println(“amount:” + amount); } void printBus(int amount){ String name = getBusName(); System.out.println(“name:” + name); System.out.println(“amount:” + amount); } void printTaxi(int amount){ String name = getTaxiName(); print(name, amount); } void printBus(int amount){ String name = getBusName(); print(name, amount); } void print(String name, int amount){ System.out.println(“name:” + name); System.out.println(“amount:” + amount); } Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 7 コードクローンに対するリファクタリング 兄弟クラス間で重複コードを共有している場合 各クラスに対して Extract Method を行い,次に Pull Up Field や Pull Up Method を適用すれば解決する 場合によっては,Form Template Method などを適用する 場合もありうる Employee Employee getName() Salesman getName() Engineer getName() Salesman Engineer Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 8 コードクローンに対するリファクタリング まったく関係のないクラス間で重複コードが見られる場合 Extract Class を適用し,古いクラスから新しいクラスへと処 理を委譲するように変更を行う Person name officeAreaCode officeNumber name getTelephoneNumber() getTelephoneNumber() Company address officeAreaCode officeNumber Person 1 Company 1 Telephon Number areaCode number 1 1 getTelephoneNumber() address getTelephoneNumber() getTelephoneNumber() Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 9 本研究で扱うコードクローン コードクローンの一般的な定義はない これまでにいくつかのコードクローン検出手法が提案されている が,それらはどれも異なった定義を持つ リファクタリング可能なコードクローンを検出したい コードクローン検出に CCFinder[3] と CCShaper[4] を用いる [3] T. Kamiya, S. Kusumoto, and K. Inoue, CCFinder: A multi-linguistic token-based code clone detection system for large scale source code, IEEE Transactions on Software Engineering, vol. 28, no. 7, pp. 654-670, Jul. 2002. [4] 肥後 芳樹,植田 泰士,神谷 年洋,楠本 真二,井上 克郎:コードクローン解析に基づくリファクタリングの試み, 情報処理学会論文誌, Vol.45, No.5, pp1357-1366, 2004-5 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 10 クローンペアとクローンセット クローンペア 互いに一致または類似しているコード片の対(ペア) クローンセット 互いに一致または類似しているコード片の集合(セット) C1 C2 C3 C4 C5 クローンペア (C1, C2) クローンセット {C1, C2, C4} (C1, C4) {C3, C5} (C2, C4) (C3, C5) Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 11 コードクローン検出ツール:CCFinder 概要 ソースコードをトークン単位で直接比較することにより,コード クローンを検出する より実用的なコードクローンを見つけることができるように設計 されている ユーザ定義名の置き換え テーブル初期化部分の取り除き モジュールの区切りの認識 検出結果はテキスト形式で出力 数百万行単位のソフトウェアからでも数十分で検出可能 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 12 コードクローン抽出ツール:CCShaper 概要 CCFinderが検出したコードクローンに含まれるプログラミング 言語の構造的なまとまりの部分を抽出 コードクローン情報をリファクタリングに用いる場合に便利 CCFinderが検出するコードクローンはトークンの並びであり,必 ずしもリファクタリングの適用が容易なまとまりにはなっていない 現在はJava言語のみに対応 抽出するコードクローンの単位は以下の12個 宣言:クラス,インターフェース 関数:メソッド,コンストラクタ,スタティックイニシャライザ 文:do, for, if, switch, synchronized, try, while, Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 13 コード片1 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: reset(); grammar = g; // Lookup make-switch threshold in the grammar generic options if (grammar.hasOption("codeGenMakeSwitchThreshold")) { try { makeSwitchThreshold = grammar.getIntegerOption("codeGenMakeSwitchThreshold"); //System.out.println("setting codeGenMakeSwitchThreshold to " + makeSwitchThreshold); } catch (NumberFormatException e) { tool.error( "option 'codeGenMakeSwitchThreshold' must be an integer", grammar.getClassName(), grammar.getOption("codeGenMakeSwitchThreshold").getLine() ); } } CCFinderが検出する クローン // Lookup bitset-test threshold in the grammar generic options if (grammar.hasOption("codeGenBitsetTestThreshold")) { try { bitsetTestThreshold = grammar.getIntegerOption("codeGenBitsetTestThreshold"); コード片2 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: } // Lookup bitset-test threshold in the grammar generic options if (grammar.hasOption("codeGenBitsetTestThreshold")) { try { bitsetTestThreshold = grammar.getIntegerOption("codeGenBitsetTestThreshold"); //System.out.println("setting codeGenBitsetTestThreshold to " + bitsetTestThreshold); } catch (NumberFormatException e) { tool.error( "option 'codeGenBitsetTestThreshold' must be an integer", grammar.getClassName(), grammar.getOption("codeGenBitsetTestThreshold").getLine() ); } } CCShaperが抽出す るクローン // Lookup debug code-gen in the grammar generic options if (grammar.hasOption("codeGenDebug")) { Token t = grammar.getOption("codeGenDebug"); if (t.getText().equals("true")) { Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 14 コード片3 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017: 1018: 1019: コード片4 if ( inputState.guessing==0 ) { buf.append(a.getText()); } { _loop144: do { if ((LA(1)==WILDCARD)) { match(WILDCARD); a=id(); if ( inputState.guessing==0 ) { buf.append('.'); buf.append(a.getText()); } } 1527: 1528: 1529: 1530: 1531: 1532: 1533: 1534: 1535: 1536: 1537: 1538: 1539: if ( inputState.guessing==0 ) { t=a.getText(); } { _loop84: do { if ((LA(1)==COMMA)) { match(COMMA); id(); if ( inputState.guessing==0 ) { t+=","+b.getText(); } } CCFinderが検出する クローンペア Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 15 実験: 概要 実験対象はオープンソースソフトウェア Ant 記述言語: Java 15バージョンのソースコードを用いた バージョン番号:1.1, 1.2, 1.3, 1.4, 1.5, 1.5.1, 1.5.2, 1.5.3, 1.5.4, 1.6.0, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5 最も古いバージョン(1.1)からコードクローン検出を行い,検出された全 てのコードクローンに対してリファクタリングを試みた リファクタリングのコストとして以下のものを用いた ソースコード修正と回帰テストに要した時間 リファクタリングの効果として以下のものを用いた CKメトリクス値の変化 総行数の増減 集約箇所の後のバージョンでの修正回数 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 16 実験: バージョン1.1に対するリファクタリング コードクローンのリファクタリングを行うために CCFinderとCCShaperを用いて50トークン以上のコードクローンを検出 バージョン1.1の規模 ソースファイル数:83 総行数:19,986 検出時間は約1分 7個のクローンセットを検出 そのうちの6つが実際にリファクタリングすることができた ソースコード修正後,付属のテストケースを用いて回帰テストを行った その後の修正回数を調査するために バージョン1.2~1.6.5の該当箇所を調査 前のバージョンから修正を加わっているかどうかを diff で調査 クローンセットになっているかを CCFinder と CCShaper を用いて調査 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 17 実験: 検出されたコードクローン 以下は検出されたコードクローンの概要である ID S0 コード片数 2 単位 メソッド 分類 単一クラス内 時間 7分 S1 S2 S3 6 2 2 メソッド メソッド メソッド 単一クラス内 単一クラス内 単一クラス内 19分 - S4 S5 S6 2 2 2 If文 メソッド メソッド 兄弟クラス間 兄弟クラス間 関係のないクラス間 14分 5分 17分 15分 実際はもっと回帰テストに時間がかかる 付属のユニットテストしか行っていない,結合テストなども行うべき Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 18 実験: CKメトリクス値の変化(1/3) S0のCKメトリクス値の変化 クラス名 DirectoryScanner DIT NOC CBO 21(+1) 1 0 0 WMC DIT NOC CBO 21(+1) 1 0 0 RFC LCOM 47(+1) 158(+20) S1のCKメトリクス値の変化 クラス名 DirectoryScanner WMC RFC LCOM 47(+1) 158(+20) S3のCKメトリクス値の変化 リファクタリング前に比べてメトリクス値が悪化 リファクタリング前に比べてメトリクス値が改善 クラス名 WMC DIT NOC CBO RFC LCOM 17(+1) 1 23 4 24(+6) 98(+16) $NestedElementHandler 4 2 0 4 14(-3) 0 $TaskHandler 4 2 0 7(-2) 21(-7) 0 Task Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 19 実験: CKメトリクス値の変化(2/3) S4のCKメトリクス値の変化 クラス名 WMC DIT NOC CBO RFC LCOM 15(+2) 1 0 7(+1) 66(+7) 79(+27) Expand 4 2 0 5 35(-3) 2 Untar 4 2 0 8 36(-3) 2 WMC DIT NOC CBO RFC LCOM Javac 23(-1) 3 1 11 93 128(-105) MatchingTask 16(+1) 2 11 5(+1) 56(+4) 94(+79) Rmic 17(-1) 3 0 8 66(-2) 98(-17) WMC DIT NOC CBO RFC LCOM NewClass 2 1 0 2 7 1 JavacOutputStream 4 1 0 2(+1) 10(-1) 0 ProjectHelper S5のCKメトリクス値の変化 クラス名 S6のCKメトリクス値の変化 クラス名 TaskOutputStream 3 1 0 2(+1) 8(-1) Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 0 20 実験: CKメトリクス値の変化(3/3) コードクローンのリファクタリングは,必ずしもCKメトリクス値を改善すると は限らない 中には,クローンセット S0 や S1 のようにメトリクス値を悪化させているだけ のリファクタリングも存在した CKメトリクスを測定するために ckjm を用いた このツールはメトリクス WMC として,単純にそのクラスで定義されたメソッ ドの数を用いている WMCは Weigthed Method per Class の略 本来は,各メソッドに対して重み付けを行った後,値を足し合わせる Method のようにメソッド数が増えるリファクタリングの場合は,必 ず値が悪化してしまう サイクロマチック数などを用いて適切に重み付けをすれば,メソッド数を増 やすリファクタリングであっても WMC の改善があるかもしれない Extract Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 21 実験: 総行数の変化 下図は各クローンセットのリファクタリング後の総行数の変化を表す リファクタリング S0後 S1後 S3後 S4後 S5後 S6後 行数 19,982 (-4) 19,964 (-22) 19,981 (-5) 19981 (-5) 19,962 (-24) 19,990 (+4) 5つのリファクタリングで総行数が減っている 多くても20行程度しか減っておらず,サイズ面で効果があるとはいい難い Class”パターンを適用したリファクタリングは,新しいクラスを作 成したために,リファクタリング前に比べて行数が増えてしまっている “Extract Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 22 実験: 修正の流れ(1/5)(クローンセット S0,S2) クローンセット S0 1.1 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 C0 C0’ C0’’ C1 C1’ C1’’ コード片は Cn で表され,修正が加わるたびに Cn’, Cn’’ と変化していく 同一クローンセットに含まれるコード片は長方形で囲まれる 1.1 C0 クローンセット S2 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 C0’ C1 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 23 実験: 修正の流れ(2/5)(クローンセット S1) 1.1 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 C0 C0’ C0’’ C0’’’ C1 C1’ C1’’ C1’’’ C6 C7’ C7 C2 C2’ C2’’ C2’’’ C3 C3’ C3’’ C3’’’ C4 C4’ C4’’ C4’’’ C5 C5’ C5’’ C5’’’ C8 C8’ C8’’ C9 C9’ C9’’ Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 24 実験: 修正の流れ(3/5)(クローンセット S3,S4) 1.1 クローンセット S3 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 C0 C0’ C0’’ C0’’’ C0’’’’ C1 C1’ C1’’ C1’’’ C1’’’’ 1.1 C0 クローンセット S4 1.2 1.3 C0’ 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 C0’’ C1 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 25 実験: 修正の流れ(4/5)(クローンセット S5,S6) 1.1 C0 クローンセット S5 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 C0’ C1 1.1 C0 クローンセット S6 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 C0’ C1 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 26 実験: 修正の流れ(5/5) リファクタリング箇所の後のバージョンでの修正回数はクローンセットに よってさまざまであった 全てのクローンセットがバージョンをまたいで存在しているわけではなく,各 コード片に対して異なる修正が加わった結果,コードクローンでなくなる場 合もあった. 本実験では7つクローンセットのうちの4つ これらのクローンセットには一度も共通の修正がなかった 一度でも共通の修正が加わった場合は,その後も修正される傾向がある 実際に全てのコード片に同様の修正が加わった場合にリファクタリングを行う ことで,その後の修正回数を減らすことができるのでは? Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 27 考察 リファクタリングが有効であるかどうかは,その効果がコストを上回ってい るかどうかで決まる 例:クローンセット S0 コスト:ソースコード修正と回帰テストに7分を要した 効果(CKメトリクス):WMC,RFC,LCOM が悪化 効果(修正回数):バージョン 1.5 と 1.6.3 での修正箇所が2つから1つに 減った 効果とコストの大小関係は一意に決めることができない 本実験で行ったリファクタリングは,基本的かつ汎用的であり,ソース コード修正を行う者のスキルや対象ソフトウェアのドメインを限定するも のではない リミテーション リファクタリングの規模が小さい メトリクス値で調査したのは複雑度の変化のみ 他の面からの評価も必要である Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 28 まとめ コードクローンに対するリファクタリングのコストと効果を調査した 実験対象は15バージョンのソースコードが公開されているオープンソー スソフトウェア その最初のバージョンに対してリファクタリングを行った コストの尺度 ソースコード修正と回帰テストに要した時間 効果の尺度 CKメトリクス値の変化 総行数の増減 集約箇所の後のバージョンでの修正回数 こらからの予定 コスト・効果項目を増やす 企業ソフトウェアでも実験を行う(+ 開発者へのインタビュー) Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 29 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 30 コードクローン検出ツール: CCFinder 検出プロセス Source files static throws {{{ String 1. static static foo() throws RESyntaxException { {{ $$ $$ (( (( )) ))throws $void void foofoo() throws$$RESyntaxException RESyntaxException String aaa { static RESyntaxException String 1. void throws RESyntaxException static void $ $$foo throws ]{{] {${$ "123,400" , [[ [][] String $String $$ = new $[] { ]]] === a[] new $[] 2. [[String a[] = new String { "123,400", "123,400", "abc", "orange "orange 100" 100" }; }; 2. new "abc", [String $String "abc" , "orange 100" } ; org . apache . regexp ; } 3. org.apache.regexp.RE org.apache.regexp.RE pat == new new org.apache.regexp.RE("[0-9,]+"); org.apache.regexp.RE("[0-9,]+"); } ; 3. pat $$ === new $$ pat RE pat RE new 4. .int int sum 0; org . apache . regexp 4. sum ==new 0; . RE )) ;;; int $$ ==== $0$00 $$ sum $$ $$ ((( "[0-9,]+" RE int sum sum 5. for for (int"[0-9,]+" i == 0; 0; ii) << a.length; a.length; ++i) 5. (int i ++i) ;; for $$ i$$i === 0$0$ ;;; i$$i <<<< int for (( int 6. a$ ifif. (pat.match(a[i])) (pat.match(a[i])) 6. ;++ $$ ii)) ))if $$ ;; ++ length ; ++ ++ pat pat $ . length if ifif(( ((($$ pat 7. .. match sum += Sample.parseNumber(pat.getParen(0)); 7. sum += Sample.parseNumber(pat.getParen(0)); $$ (( (( $$ aa [[ [[ $$ ii ]] ]] )) )) )) ))) $$sum sum sum . match 8. System.out.println("sum " ++ sum); sum); 8. += System.out.println("sum "getParen += Sample ((( 000 (( $$ .. $$ (( ((pat $$ .. $.$. parseNumber parseNumber pat$$ .= . getParen pat .= getParen += 9. }} )) )) ;; System (( $$ ((( "sum .. .$$. println $$ .. .$$. out System out println "sum===""" 9. println "sum static String $$ $$goo }}} static ))) ;;; goo(String $$ void sum static void void goo String 10. static static void goo(String []((a) a)((( $$throws throws RESyntaxException {{ +++ sum goo String 10. [] RESyntaxException static {{ $$ $$ = $$RE("[0-9,]+"); throws RESyntaxException RE exp exp === [[[ exp ]]] )))= RESyntaxException RE exp 11. a$a$RE RE exp =throws newRESyntaxException throws = {{{ RE 11. new RE("[0-9,]+"); new RE = $$ )) ;;)) $$;; $$int $$ (( "[0-9,]+" int sum sum new =sum$$=== 000 12. new int sum"[0-9,]+" 0; 12. int sum == 0; $$ i$$i === 0$0$ ;;; i$$i <<<< for int for ((( int for (int 13. ;;;for for 0; ii << a.length; a.length; ++i) ++i) 13. (int ii == 0; $ ( $$ ii)) ))if $$ ;; ++ if ( exp length ; ++ ++ if ( exp a$a$ ... length ;++ ( exp if ( $ 14. . match (exp.match(a[i])) 14. ifif (exp.match(a[i])) ] $ [ $ ( $ ( a [ i ] ( a [ i ] sum sum . $ ( $ [ $ ] )) )) )) ))) $$sum 15. += sum += parseNumber(exp.getParen(0)); 15. sum += parseNumber(exp.getParen(0)); (( .. $$ getParen $$ (( ( $$exp.. ((. $$exp += getParen ()) 0)) )(( )00 )) )) parseNumber exp getParen $$$ ... parseNumber += parseNumber 16. ;;;System.out.println("sum System.out.println("sum ==="""""+++++sum sum); 16. sum); $$ = (( $$ ((++ "sum .. .$$. println $$ .. .$$. out = System out println "sum sum System "sum sum 17. }}))) ;;; }}} 17. 字句解析 字句解析 トークン列 トークン列 変換処理 変換処理 変換後トークン列 変換後トークン列 検出処理 検出処理 クローン情報 クローン情報 出力整形処理 出力整形処理 クローンペア位置情報 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 31 リファクタリングできなかったクローンセット S2 コード片 C0 protected void fireTargetStarted(Target target) { BuildEvent event = new BuildEvent(target); for (int i = 0; i < listeners.size(); i++) { BuildListener listener = (BuildListener) listeners.elementAt(i); listener.targetStarted(event); } } コード片 C1 protected void fireTaskStarted(Task task) { BuildEvent event = new BuildEvent(task); for (int i = 0; i < listeners.size(); i++) { BuildListener listener = (BuildListener) listeners.elementAt(i); listener.taskStarted(event); } } Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 32 Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 33
© Copyright 2024 ExpyDoc