Upload
emma
View
39
Download
0
Embed Size (px)
DESCRIPTION
細かい粒度 で コードの再利用を可能とする メソッド内メソッドと その効率の良い実装方法の提案. 平松 俊樹 千葉 滋 東京工業 大学 数理・計算科学専攻. メソッド の一部を 切り出す. メソッドの一部を 別メソッドに 例 . Eclipse での リファクタリング extract method 分割 再利用. class Max { int calc ( int [] a) { : for ( int i = 0; ..) { sum += a[i]; if (max < a[i]) - PowerPoint PPT Presentation
Citation preview
細かい粒度でコードの再利用を可能とする
メソッド内メソッドとその効率の良い実装方法の提案
平松 俊樹 千葉 滋
東京工業大学数理・計算科学専攻 1
メソッドの一部を切り出す
• メソッドの一部を 別メソッドに
例 . Eclipse での リファクタリング extract method–分割–再利用
2
class Max { int calc (int[] a) { : for (int i = 0; ..) { sum += a[i]; if (max < a[i]) max = a[i]; } : }}
メソッドの一部の再利用
• 切り出したメソッドをサブクラスで上書き
• メソッドの一部分を変更–実際は難しい
3
class Max { int calc (int[] a) { int sum = 0; int max = a[0]; int average; for (int i = 0; ..) { calcSum(sum, max, i, a); } average = sum / a.length; return max – average; }}
class Min extends Max { void calcSum(int sum, int max, int i, int[] a) { sum = .. }}
切り出しは困難
• ローカル変数の参照–大量の引数
• 変数への代入は?
4
class Max { int calc (int[] a) { : for (int i = 0; ..) { calcSum( sum, max, i, a); } : }
void calcSum( int sum, int max, int i, int[] a) { sum += a[i]; : }}
提案:上書き可能なメソッド内メソッド
• ローカル変数にアクセス可能• サブクラスで上書き可能
5
class Max { int calc (int[] a) { public int max, .. for(int i=0; ..) { void calcSum( int[] a, int i) { sum += a[i]; if (max < a[i]) max = a[i]; } calcSum(a, i); } : }}
class Min extends Max { void calc (int[]). calcSum(int[] a, int i) { sum += a[i]; if (max > a[i]) max = a[i]; }}
上書き
メソッド内メソッドの定義
• メソッドボディにメソッド定義を記述• 定義だけでは呼ばれない
public については後述
6
class Max { int calc (int[] a) { public int max, .. for (int i = 0; ..) { void calcSum(int[] a, int i) { sum += a[i]; if (max < a[i]) max = a[i]; } calcSum(a, i); } : }}
メソッド内メソッドの上書き
• メソッド名を “ . ” で区切って指定
7
class Min extends Max { void calc (int[]).calcSum(int[] a, int i) { sum += a[i]; if (max > a[i]) max = a[i]; }}
ローカル変数の参照
• 外側のメソッドの全ローカル変数が メソッド内メソッドから参照、代入可能– メソッド内メソッドを上書きしていないクラス– グローバル変数のように見える
8
class Max { int calc (int[] a) { public int sum = 0; public int max = a[0]; int average; for (int i = 0; ..) { void calcSum(int[] a, int i) { sum += a[i]; if (max < a[i]) max = a[i]; } calcSum(a, i);}}}
public 変数
• 上書き後は public 変数だけが参照、代入可能–非 public 変数は参照も代入も不可–カプセル化
9
class Max { int calc (int[] a) { public int sum = 0; public int max = a[0]; int ave; for(int i = 0;..){ void calcSum( int[] a, int i) { sum += a[i]; if (max < a[i]) max = a[i]; } calcSum(a, i); } : }}
class Min extends Max { void calc (int[]). calcSum(int[] a, int i) { sum += a[i]; if (max > a[i]) max = a[i]; }}
メソッド内メソッドのスコープ
• メソッド内メソッドの有効範囲–ひとつ外側のメソッドのボディ–自身のメソッドボディ
10
void f() { void g() {} g();}
void f() { void g() { g(); }}
void f() {
void g() {
void h() {
}
}
}
.. void g() { .. void h() {
} .. } ..
void h() {
}
他の方法 : 参照渡し
• C++ における参照渡し–変数への代入が可能
–大量の引数– Java には無い–呼ぶ側から値渡しと
区別がつかない• 副作用の有無
11
class Max { int calc (int[] a) { : for (int i = 0; ..) { calcSum( sum, max, i, a); } : } void calcSum( int& sum, int& max, int i, int[] a) { : }}
他の方法 : クロージャ
• ローカル変数にアクセスできる–上書きするとアクセスできない
12
class Max { Closure calcSum; int calc (int[] a) { int max, .. for (int i = 0; ..) { calcSum = { if (max < a[i]) max = a[i]; } calcSum(); } : }}
class Min extends Max { int calc (int[] a) { calcSum = { if (max > a[i]) max = a[i]; } : }}
外側のメソッドのコード変換
• 外側のメソッドを2種類用意–メソッド内メソッドをインライン展開したもの–展開しないもの• メソッド内メソッドは通常のメソッドに変換• サブクラスでメソッド内メソッドを上書き• サブクラスからはこちらが呼ばれる
13
int calc(int[] a) { Var$calc $var = new Var$calc(); $var.sum = ..; : calcSum($var);}void calcSum(Var$calc $var) { :
呼び出しを展開していない
メソッド内メソッドを通常のメソッドに変換
効率的な実装
• ソースコードを変換• メソッド内メソッドをインライン展開–再帰呼び出しが無く、上書きされないコードの場
合–メソッド呼び出しのオーバーヘッドが消える
14
int calc(int[] a) { void calcSum(int[] a, int i) { sum += a[i]; if (max < a[i]) max = a[i]; } for (int i = 0; ..) { calcSum(a, i); }}
int calc(int[] a) { for (int i = 0; ..) { sum += a[i]; if (max < a[i]) max = a[i]; }}
展開
外側のメソッドのコード変換
• メソッド内メソッドをインライン展開しない
15
int $calc(int[] a) { Var$calc $var = new Var$calc(); for (int i = 0; ..) { calcSum(a, i, $var); }}
void calcSum (int[] a, int i, Var$calc $var){ $var.sum += a[i]; if ($var.max < a[i]) $var.max = a[i];}
class Var$calc { int sum; int max; ..}
メソッド内メソッドを通常のメソッドに変換
ローカル変数を集めたクラスを作成
メソッド内メソッドの上書きの実装
• 通常のメソッドに変換–スーパークラスにおいて通常のメソッドに変
換されたメソッド内メソッドを上書き–変数へのアクセスはオブジェクトを介す
16
class Min extends Max { void calc (int[] a). calcSum(int[] a, int i) { sum += a[i]; if (max > a[i]) max = a[i]; }}
class Min extends Max { void calcSum( int[] a, int i, $Var $var){ $var.sum += a[i]; if ($var.max > a[i]) $var.max = a[i]; }}
変換
実験 : マイクロベンチマーク• 実行時間・コード量の比較
– 本システムを用いたコード– 通常の Java でメソッドを切り出さない– 通常の Java でメソッドを切り出す– 上書きの有無– 100,000,000回実行
• 実験環境– OS: Windows 7– CPU: Intel Core i5 2.67GHz– メモリ: 4GB
17
class Max { int calc (int[] a) { int sum = 0; int max = a[0]; int ave; for (int i = 0; ..) { sum += a[i]; if (a[i] > max) max = a[i]; } ave = sum / a.length; return max – ave; }}
実験結果・実行時間–本システムを用いても、上書
き前はメソッドに切り出さない場合と差が無い
–上書き後であっても、別のメソッドを定義した場合よりは速い• 効率よく書くのは難しい
–切り出し• 初めから別メソッドとして定
義• extract a method 18
本システム
切り出しなし
切り出しあり
0
1000
2000
3000
4000
5000
6000
7000
8000
1454 1472
7295
5103
1386
7585
元のクラスサブクラス
ミリ秒
実験結果・コード量–本システムでは、差分のみ
の記述で変更が可能であるため、上書き時のコード量が少なくなる
19
本システム
切り出しなし
切り出しあり
0
5
10
15
20
25
30
16
13
26
6
13
10
元のクラスサブクラス
行数
関連研究
• Regioncut [Akaiら’ 09]– コード領域をジョインポイントとして選択– コード領域に対する変更が可能– ローカル変数への代入が不可能
• Closure Joinpoints [Bodden ’11]– コードブロックをジョインポイントとして選択– ローカル変数への代入が不可能
• Beta [Knudenら’ 94]– オブジェクト指向言語– 上書き可能なインナープロシージャ
• スーパークラスの振る舞いが取り除けない
20
まとめと今後の課題
• まとめ– メソッド内メソッド
• 上書き• ローカル変数を参照
– 効率的な実装• インライン展開
• 今後の課題– return の扱い
• メソッド内メソッドから?• 外側のメソッドから?
21