CKメトリクスに基づくリファクタリングの 効果予測手法の提案と実装 大阪大学 大学院情報科学研究科 ○松本義弘 肥後芳樹 楠本真二 井上克郎
研究の背景 将来的なソフトウェアの保守コストの削減を目的に,リファクタリング[1]と呼ばれる技術が研究されている. リファクタリングとは? ソフトウェアの外部的振る舞いを保ったまま,内部の構造を改善する技術 リファクタリングの効果は? ソフトウェアの保守性が向上する リファクタリングを実施するプロセスで,その効果を予測したいという要求がある まず,研究の背景ですが,将来的なソフトウェアの保守コストの削減を目的に,リファクタリングと呼ばれる技術が研究されています. リファクタリングとは,ソフトウェアの外部的振る舞いを保ったまま,内部の構造を改善する技術のことであり,リファクタリングを適用することで,ソフトウェアの保守性が向上すると言われています. また,リファクタリングを実施するプロセスで,その効果を予測したいという要求があります. 次に,一般的に用いられているリファクタリングのプロセスを紹介します. [1] M. Fowler, Refactoring: improving the design of existing code, Addison Wesley, 1999.
研究対象と目的 一般的に使われるリファクタリングプロセス[2] Step 1: どこにリファクタリングを適用すべきか決定する Step1で,どこにリファクタリングを適用するか,Step2でどのようなリファクタリングを適用すべきかを決定します. Step3で,決定したリファクタリングの効果を予測し,Step4では,実際にプログラムを修正します. 最後のStep5では,修正したプログラムが正しく動作するかのテストを行います. 本研究では,Step3リファクタリングの効果予測を支援することを目的とします. [2] T Mens and T. Tourwé. A survey of software refactoring. IEEE Trans. Softw. Eng., 30(2):126–139, 2004.
関連研究 リファクタリング位置特定/選択手法 リファクタリングの効果計測手法 構造的な欠陥(低凝集・高結合のクラスなど)を発見し,それに対して効果的なリファクタリングを提供[3] リファクタリングの効果計測手法 高結合を解消するリファクタリングを,独自に定義した結合度メトリクスに基づいて定量的に評価する[4] 関連研究としましては,リファクタリングの位置特定手法と選択手法があります. 具体的には,低凝集・高結合といった構造的な欠陥をもつクラスを発見し,それに対して効果的なリファクタリングを提案しています. また,リファクタリングの効果を計測する手法としましては, 高結合を解消するリファクタリングを独自に定義した結合度メトリクスに基づいて定量的に評価するものがあります. [3]秦野, 乃村, 谷口, 牛島. ソフトウェアメトリクスを利用したリファクタリングの自動化支援機構. 情報処理学会論文誌,44(6):1548–1557, Jun 2003. [4]Y. Kataoka. T. Imai, H.Andou and T. Fukaya, A quantitative evaluation of maintainability enhancement by refactoring., In ICSM ’02, p. 576-585, 2002.
研究の概要 関連研究の問題 研究目的 リファクタリングは,高結合・低凝集なクラスだけが対象ではない 「計算処理を行うクラスに画面描画の機能の一部が実装されているので適切なクラスに移動したい」 このようなケースにおいても,リファクタリングを評価する必要がある リファクタリング適用支援ツールを使っても,ソースコードの修正はコストがかかる ソースコードを修正する前に,ソフトウェア保守に与える効果を知るべき 研究目的 ソースコードを修正する前に,リファクタリングが保守性に与える効果を予測する手法を提案・実装し,有用性を評価する これらの関連研究の問題としましては,リファクタリングは,高結合・低凝集なクラスだけが対象ではないということが挙げられます. 例えば, 「計算処理を行うクラスに画面描画の機能の一部が実装されているので適切なクラスに移動したい」というケースがあります. このようなケースにおいても,リファクタリングを評価する必要があります. また,リファクタリング適用支援ツールを使っても,ソースコードの修正はコストがかかるため,ソースコード修正前にソフトウェア 保守に与える効果を知るべきだと考えています. そこで,本研究の目的は,ソースコードを修正する前に,リファクタリングが保守性に与える効果を予測する手法を提案・実装し, 有用性を評価することです. 次にソフトウェアの保守性に関連する研究を紹介します.
ソフトウェアの保守性 「機能仕様の変更や追加に対する修正のしやすさ」を表す ソフトウェアの保守性を評価するためにソフトウェア複雑度メトリクスが用いられている[4]. メトリクスが高く複雑であればあるほど修正のコストがかかり,保守が困難であると評価されている 代表的なソフトウェア複雑度メトリクスとして,CKメトリクスがあげられる 次にソフトウェアの保守性に関して簡単に紹介します. ソフトウェアの保守性とは, 「機能仕様の変更や追加に対する修正のしやすさ」を表します. また,ソフトウェアの保守性を評価するために,ソフトウェア複雑度メトリクスが用いられます. ソフトウェア複雑度メトリクスが高く複雑であればあるほど修正のコストがかかり,保守が困難であると評価されています. 代表的なソフトウェアメトリクスとして,CKメトリクスがあげられます. [4] 神谷年洋, オブジェクト指向メトリクスを用いた開発支援法に関する研究, PhD thesis, 大阪大学大学院基礎工学研究科, 2001.
※これらのメトリクスは,値が高いほどクラスが複雑であることを表す CKメトリクス CKメトリクスは以下の3つの観点からクラスの複雑さを評価する 継承 DIT (Depth of inheritance tree) NOC (Number of children) 結合 RFC(Response for a class) CBO(Coupling between object-class) クラスの 内部複雑度 WMC(Weighted methods per class) LCOM(Lack of cohesion in method) CKメトリクスは以下3つの観点から継承,結合,内部的な複雑さ,から,クラスの複雑さを評価します. 最初の2つ,DITとNOCはクラスの継承に関する複雑さを計測します. DIT(Depth…)は計測対象のクラスの継承木内の深さを数えます. NOC(Number…)は計測対象のクラスから直接導出されているクラスの数を数えます. 次の2つ,RFCとCBOはクラス間の結合に関する複雑さを計測します. RFC(Response…)はあるクラスが呼び出すメソッドの数を数えます. CBO(Coupling…)はクラスがメソッドまたは変数によって他のクラスを参照している数を数えます. 最後の2つ,WMCとLCOMはクラスの内部的な複雑度を計測します. WMC(Weighed…)はクラスのメソッドの複雑さを合計したものです. メソッドの複雑度の算出方法に関しては,いくつか方法が提案されていますが,本発表ではサイクロマチック数を用いることにしました. LCOM(Lack…)はクラスのメソッドについての凝集度の欠如を計測します. ※これらのメトリクスは,値が高いほどクラスが複雑であることを表す 本研究では,CK メトリクスを用いてリファクタリングがソフトウェア保守に与える効果を予測する手法を提案する
提案手法の概要 System プログラム解析 解析情報α CK Metricsα 解析情報β CK Metricsβ STEP1 Program 解析情報α CK Metricsα STEP1 リファクタリングの箇所/リファクタリングパターン STEP2 リファクタリング箇所とパターンを取得 リファクタリング前後のメトリクスの変化率の計算 STEP4 解析情報β STEP3 次に提案手法の概要について説明します. まず,ユーザはシステムにプログラムを入力として与え,システムは与えられたプログラムを解析します. そして,システムは解析情報を取得し,その解析情報からCKMetricsを計測します. 次に,ユーザはリファクタリングしたい箇所とリファクタリングパターンを与えます. そして,システムは取得したリファクタリング箇所とパターンを解析情報に反映させます. 最後に,修正された解析情報からCKメトリクスを計測し,リファクタリング前後のメトリクスの変化率をユーザにフィードバックするといった流れになっています. 次に,このSTEP1から4に関して,詳しく説明していきます. CK Metricsβ
STEP1:対象プログラムの解析 対象プログラムの解析を行い,CKメトリクスを計測する. CKメトリクス計測 解析情報 ソフトウェアを構成する全クラス 解析情報 ・クラス間の参照関係 ・メソッド間の参照関係 ・メソッドとフィールドの参照関係 ・メソッド内の分岐・繰り返しの数 など D B A F E まず,STEP1では,対象プログラムの解析を行い,CKメトリクスの計測を行います. 解析して得られる情報としましては, ・クラス間の参照関係 ・メソッド間の参照関係 ・メソッドとフィールドの参照関係 ・メソッド内の分岐と繰り返しの数 などが挙げられます. これらの情報を元に,CKメトリクスの計測を行います. C G CKメトリクス計測
STEP2:リファクタリング箇所とリファクタリングパターンの入力 リファクタリングしたい箇所(フィールド,メソッド,クラス)を指定し,適用したいリファクタリングパターンを与える 実装しているリファクタリングパターン フィールドの移動 フィールドの引き上げ フィールドの引き下げ メソッドの移動 メソッドの引き上げ メソッドの引き下げ クラスの抽出 スーパークラスの抽出 サブクラスの抽出 次にSTEP2ですが,ユーザはリファクタリングしたい箇所,フィールド,メソッド,クラスを指定し,適用したいリファクタリングパターンをシステムに与えます. 本研究で実装しているリファクタリングパターンはこの9つで,構造的な変化を伴うリファクタリングパターンの一部を実装しています.
STEP3:解析情報の修正(1) STEP2を元に解析情報を修正する 例:クラスAのメソッドa1()をクラスBへ移動 (1)リファクタリングの影響を受けるクラスを検出する (2)ユーザ非依存部分の修正 (3)ユーザ依存部分の修正 D B クラス メソッド Fan-in A a1() B b1() C c1() D d1() a2() ・・・ d1( ) call a1( ) b1( ) STEP2で入力された情報をもとに,解析情報を修正します. ここで,クラスAのメソッドa1()をクラスBへ移動するという例を用いて説明します. メソッドa1()は,メソッドb1,c1,d1から参照されているとすると,メソッドb1,c1,d1が属するクラスB,C,Dが影響を受けるクラスとなります. call a1( ) A a1( ) a2( ) C call a1( ) c1( )
STEP3:解析情報の修正(2) STEP2を元に解析情報を修正する 例:クラスAのメソッドa1をクラスBへ移動 (1)リファクタリングの影響を受けるクラスを検出する (2)ユーザ非依存部分の修正 (3)ユーザ依存部分の修正 A B C a2( ) D call a1( ) d1( ) a1() b1() c1( ) A B C a1( ) a2( ) D call a1( ) d1( ) b1( ) c1( ) クラス メソッド A a2() メソッドb1() Offset 命令 10 (ClassA)instA.a1() a1() ~ 次に,解析情報の修正を行います. 解析情報の修正は,ユーザ非依存部分とユーザ依存部分の2段階に分けられます. ユーザ非依存部分の修正は,システムが機械的に変換できるものに限られます. 例えば,メソッドa1()はクラスAに属しているという情報をクラスBへ移動すること, また,メソッドb1()がクラスAを参照していたのをクラスB自身の参照に変換するといった処理です. クラス メソッド B b1() (ClassB)a1() ~ a1()
STEP3:解析情報の修正(3) STEP2を元に解析情報を修正する どのインスタンスに対して呼び出すのか? 例:クラスAのメソッドA1をクラスBへ移動 (1)リファクタリングの影響を受けるクラスを検出する (2)ユーザ非依存部分の修正 (3)ユーザ依存部分の修正 メソッドd1() Offset 命令 20 (ClassA)instA.a1() D B call a1( ) d1( ) a1() b1() ~ 次に,ユーザ依存部分の修正です. メソッドd1()は,元々クラスAを参照していましたが,a1()を移動したため,クラスBに参照先を変更する必要があります. そこで,メソッドd1()内では,メソッドa1()をクラスBのどのインスタンスに対して呼び出すのかを決定しなければなりません. その際,ユーザに入力を求め,得た情報を元にどのインスタンスに対して呼び出すのかを決定します. A (ClassB)???.a1() call a1( ) a2( ) C ~ c1( ) どのインスタンスに対して呼び出すのか?
WMCの変化率= SUMWMC(A’, B’, D’, F’)- SUMWMC(A, B, D, F) SUMWMC(A, B, D, F) STEP4:CKメトリクスの計算 修正後の解析情報を元にCKメトリクスを計測 CKメトリクスの変化があったクラスに対し,メトリクスの変化率を計算 ソフトウェアを構成するクラス群 変化があったクラスのCKメトリクス WMC A’ CBO ・・・ xx DIT B’ D’ F’ 合計 D’ A B F A’ B’ F’ D B A F 変化率 xx xx ・・・ xx E STEP4では,修正後の解析情報を元にCKメトリクスの計測を行います. そして,CKメトリクスの変化があったクラスに対し,メトリクスの変化率を計算します. ここで,クラスA,B,D,Fがメトリクスの変化のあったクラスとし,それぞれのメトリクスの変化率を求めます. WMCの変化率は(リファクタリング後のクラスABDFのWMCの合計)-(リファクタリング前のクラスABDFのWMCの合計)をリファクタリング前のクラスABDFのWMCの合計で割ったものです. WMCの変化率= SUMWMC(A’, B’, D’, F’)- SUMWMC(A, B, D, F) SUMWMC(A, B, D, F) C G
提案手法の実装 提案手法の有用性を確認するために手法の実装,適用実験を行う 実装 適用実験の対象 開発言語:Java 実験対象:Java 構成 プログラム解析部(Class Construction Kit[6]) CKメトリクス計算部(ckjm[7]) 結果出力部 適用実験の対象 Feature Location Visualizer[8](以下,FLV) クラス数:37 行数:4815行 次に,提案手法の有用性を確認するために手法の実装および適用実験を行います. 実装するにあたって,開発言語はJava,また実験対象とするソフトウェアもJavaとしました. また,システムは, ・プログラム解析部 ・CKメトリクス計算部 ・結果出力部 の3つから構成されており,プログラム解析部は”Class Construction Kit”,CKメトリクス計算部は”ckjm”というオープンソースプログラムをベースに開発しました. 実験対象のプログラムとしましては,”Feature Location Visualizer”というソフトウェアを用いました. そのプログラムの規模は,クラス数37,総行数4815行です. [6]Class Construction Kit, http://bcel.sourceforge.net/cck.html [7]ckjm. http://www.spinellis.gr/sw/ckjm/ [8]檜皮祐希,松下誠,井上克郎,更新履歴情報と静的情報を用いて同一機能を実装しているクラス群を抽出する手法の提案,電子情報通信学会技術研究報告, SS2006-67, Vol.106, No.427, pp.13-18, 2006.
適用実験の概要 問題点 改善案 そのフィールドとメソッドを以下のいずれかのクラスに移動する GUIの処理と無関係なComponentList(CL)クラスにGUIの処理の一部が実装されている FLGVV CP BP GVP CL setExtractPanel() setStartClass() ExtractPanel ep; GUI 改善案 そのフィールドとメソッドを以下のいずれかのクラスに移動する case1: FeatureLocationGraphVisualizationViewer(FLGVV)クラス case2: BirdPanel(BP)クラス case3: ComponentPanel(CP)クラス case4: GraphViewPanel(GVP)クラス 適用実験の概要ですが, 適用実験の対象ソフトウェアの中で, GUIの処理と無関係なComponentListクラスにGUIの処理の一部が実装されているという問題がありました. 具体的には,ExtractPanel型の変数とそれを参照するsetExtractPanelメソッドとsetStartClassメソッドがありました. 改善案としましては,このフィールドとメソッドをGUIの処理を行う FLGVVクラス,BPクラス,CPクラス,GVPクラスのいずれかに移動するという案が挙がりました. そこで,これらのクラスへ移動する4つのケースに対してリファクタリングの効果を予測するという実験を行いました.
適用実験の結果(1) 「フィールド,メソッドの移動」は,継承の変化を伴わないのでNOC,DITは変化しない LCOMは,getter/setterは省いて計測する仕様を採用したため,値に変化がなかった. WMC,CBO,RFCは変化が見られた. WMC:変化なし CBO:減少 RFC:微量な変化 適用実験の結果です. この表は,case1のリファクタリング後のメトリクス値とその値の増減値を括弧の中に載せています. また,各項目の青は,メトリクスが減少し複雑度が改善したもの,赤はメトリクスが増加し悪化したものを表します. さらに,WMCの合計と変化率を緑で色づけしているのは,各クラスのメトリクスに変化が見られたのですが, 合計は変化しなかったということを表しています. 今回適用した,「フィールド,メソッドの移動」は,継承の変化を伴わないのでNOC,DITは変化しませんでした. また,LCOMは,getter/setterは省いて計測する仕様を採用したため,値に変化がなかった. WMC,CBO,RFCは変化が見られた. WMCは,合計に変化がなく,CBOは減少,RFCは微量に増加するという結果が得られました.
適用実験の結果(2) 変化率をクラス数で正規化し,case1~4を比較. case1 case2,3,4 WMCは変化なし RFCは微量な変化 case1 リファクタリング後のCBOの値は減少し,一番良い結果が得られた. case2,3,4 リファクタリング後のCBOは増加してしまった. 次に,case1-4で,メトリクスの変化率をクラス数で正規化し,それぞれ比較を行いました. この結果から,すべてのcaseにおいて,WMCは変化がなく,RFCは微量な変化という結果が得られました. また,case1においては,リファクタリング後のCBOの値は減少し,他のリファクタリングよりも良い結果が得られました. case2,3,4においては,リファクタリング後のCBOの値は増加してしまうという結果が得られました.
考察 開発者の主観でリファクタリングを行うと,case2,3,4のようにソフトウェアが構造的に悪化する危険があるため,本手法を用いて比較することは重要 本手法では,リファクタリング前後のメトリクスの値だけでリファクタリングを評価しているが,より正確に評価するためには,ソースコード修正にかかるコストやテストにかかるコストも考慮するべき 最後に考察ですが, 開発者の主観でリファクタリングを行うとcase2,3,4の場合を適用してしまう可能性があり,ソフトウェアが構造的に悪化する危険があるため本手法を用いて比較することは重要であると考えています. しかし,本手法では,リファクタリング前後のメトリクスの値だけでリファクタリングを評価していますが,より正確に評価するためには,ソースコードの修正にかかるコストやテストにかかるコストも考慮するべきだと考えています.
まとめ まとめ 今後の課題 リファクタリングがソフトウェア保守に与える効果を予測する手法を提案した. その手法の適用実験を行い,有用性を評価した. 複数のリファクタリング候補を与え,それらの評価の比較を行った. 今後の課題 「クラスのインライン化」など,構造的な変化を伴う未実装なリファクタリングに関しても実装したい ソースコード修正にかかるコスト,テストにかかるコストを考慮し,手法を改良したい 最後にまとめです. (スライドに書いてることを言う)