細かい粒度で コードの再利用を可能とする メソッド内メソッドと その効率の良い実装方法の提案

Slides:



Advertisements
Similar presentations
アルゴリズムとプログラミン グ (Algorithms and Programming) 第6回:クラスとインスタンス クラスの宣言 アクセス修飾子 インスタンスの生成 (new キーワード) this キーワード フィールドとメソッドの実際の定義と使い 方 クラスの宣言 アクセス修飾子 インスタンスの生成.
Advertisements

オブジェクト指向 言語 論 第八回 知能情報学部 新田直也. 多相性(最も単純な例) class A { void m() { System.out.println( “ this is class A ” ); } } class A1 extends A { void m() { System.out.println(
独習JAVA Chapter 6 6.6 クラスの修飾子 6.7 変数の修飾子 結城 隆. 6.6 クラスの修飾 abstract インスタンス化できないクラス。1つまたは複数のサブクラスで 実装してはじめてインスタンス化できる。 final 継承されたくないことを明示する。これ以上機能拡張 / 変更でき.
1 プリミティブ Web サービスの 入出力データに関する一考察 2005 年 3 月 21 日 松江工業高等専門学校 情報工学科 奈良先端科学技術大学院大学 情報科学研究科 越田高志 電子情報通信学会 2005年総合 大会.
シーケンス図の生成のための実行履歴圧縮手法
6.4継承とメソッド 6.5継承とコンストラクタ 11月28日 時田 陽一
Javaのための暗黙的に型定義される構造体
アルゴリズムとデータ構造1 2007年6月12日
アルゴリズムとプログラミング (Algorithms and Programming)
情報伝播によるオブジェクト指向プログラム理解支援の提案
アルゴリズムとデータ構造 2011年6月13日
アルゴリズムとプログラミング (Algorithms and Programming)
社会人学習講座 「Javaプログラミング概論」
AspectScope によるアスペクトとクラスのつながりの視覚化
リファクタリングのための 変更波及解析を利用した テスト支援ツールの提案
同期的にアドバイスを活性化できる分散動的アスペクト指向システム
第6回独習Javaゼミ 第6章 セクション4~6 発表者 直江 宗紀.
細かい粒度でコードの再利用を可能とするメソッド内メソッドのJava言語への導入
ソフトウェア工学 知能情報学部 新田直也.
プログラミング言語入門 手続き型言語としてのJava
アルゴリズムとプログラミング (Algorithms and Programming)
第9章 例外処理,パッケージ 9.1 例外処理 9.2 ガーベッジコレクション.
独習JAVA 6.8 コンストラクタの修飾子 6.9 メソッドの修飾子 6.10 ObjectクラスとClassクラス 11月28日(金)
オブジェクト指向 プログラミング 第十一回 知能情報学部 新田直也.
第7回独習Javaゼミ セクション 1~4 発表者 直江 宗紀.
オブジェクト指向 プログラミング 第十三回 知能情報学部 新田直也.
静的型付きオブジェクト指向言語 のための 暗黙的に型定義されるレコード
暗黙的に型付けされる構造体の Java言語への導入
オブジェクト指向プログラムにおける エイリアス解析手法の提案と実現
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
横断的関心事に対応したオブジェクト指向言語GluonJとその織り込み関係の可視化ツール
動的データ依存関係解析を用いた Javaプログラムスライス手法
オブジェクト指向言語論 第十一回 知能情報学部 新田直也.
一時的な型 長谷川啓
アスペクト指向言語のための 独立性の高いパッケージシステム
オブジェクト指向 プログラミング 第十ニ回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
オブジェクト指向プログラミングと開発環境
再帰的手続き.
オブジェクト指向言語論 第十一回 知能情報学部 新田直也.
オブジェクト・プログラミング 第8回.
コードクローン分類の詳細化に基づく 集約パターンの提案と評価
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
アルゴリズムとプログラミング (Algorithms and Programming)
オブジェクト指向 プログラミング 第九回 知能情報学部 新田直也.
プログラミング言語論 第十三回 理工学部 情報システム工学科 新田直也.
ソフトウェア工学 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
プログラミング言語論 第十一回 理工学部 情報システム工学科 新田直也.
統合開発環境によって表現された 言語機構によるコードのモジュール化
IDE を活用した言語機構に頼らないコード再利用のためのモジュール化
同期処理のモジュール化を 可能にする アスペクト指向言語
C#プログラミング実習 第3回.
アルゴリズムとデータ構造 2012年6月11日
「マイグレーションを支援する分散集合オブジェクト」
アスペクト指向言語のための視点に応じた編集を可能にするツール
プログラムの差分記述を 容易に行うための レイヤー機構付きIDEの提案
11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数
第5回 プログラミングⅡ 第5回
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
メンバとメソッド C言語の構造体 変数の集まり C#言語のクラス + それを処理する関数の集まり フィールド または メンバ変数 メンバ
JAVA入門⑥ クラスとインスタンス.
ソフトウェア工学 知能情報学部 新田直也.
Javaとは Javaとはオブジェクト指向言語でJava VM(Java仮想マシン)と呼ばれるプログラム上で動作します。
オブジェクト指向言語論 第三回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
値渡しと参照渡しについて.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
Presentation transcript:

細かい粒度で コードの再利用を可能とする メソッド内メソッドと その効率の良い実装方法の提案 平松 俊樹 千葉 滋 東京工業大学 数理・計算科学専攻

メソッドの一部を切り出す メソッドの一部を 別メソッドに 例. Eclipse での リファクタリング extract method 分割  別メソッドに 例. Eclipse での  リファクタリング extract method 分割 再利用 class Max { int calc (int[] a) { : for (int i = 0; ..) { sum += a[i]; if (max < a[i]) max = a[i]; } プログラミングをしていて、メソッドの一部を別のメソッドに切り出したい時があります。 これはたとえばeclipseにおけるrefactoring機能のextract methodのようなことをしたい場合です。 自分がプログラミングをしているときにも、 例えばループの中の処理が複雑になるような場合には、その部分を別のメソッドに切り出したいことがあります。 ですが、切り出したい部分が多くのローカル変数を参照している場合には、大量の引数が必要 別メソッドから元のメソッドのローカル変数への代入ができない 従って別のメソッドに切り出すのは困難 切り出す前のコード

メソッドの一部の再利用 切り出したメソッドをサブクラスで上書き メソッドの一部分を変更 実際は難しい 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 = .. } また、メソッドの一部を切り出すことができれば、 その切り出されたメソッドをサブクラスで上書きすることで、 メソッドの一部分だけを変更して、 残りのコードを再利用することができます。 しかし、先にも述べたように、メソッドの一部を別のメソッドに切り出すのは難しいため、 このような再利用も現実には難しい

切り出しは困難 ローカル変数の参照 変数への代入は? 大量の引数 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]; ローカル変数の参照 大量の引数 変数への代入は?

提案:上書き可能なメソッド内メソッド ローカル変数にアクセス可能 サブクラスで上書き可能 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については 後述 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); : メソッド内メソッドの定義について説明します。 本システムでは、メソッドボディにメソッド定義を記述することができます。 以下、メソッド内メソッドをその内側に含むようなメソッドをメソッド内メソッドの外側のメソッドと呼びます。 この例では、メソッドcalcが外側のメソッドにあたります。 また、例の中にpublic の付いた変数がありますが、これについては後で説明します

メソッド内メソッドの上書き メソッド名を “ . ” で区切って指定 class Min extends Max { void calc (int[]).calcSum(int[] a, int i) { sum += a[i]; if (max > a[i]) max = a[i]; } メソッド内メソッドの上書き方法について説明します。 メソッド内メソッドは、その外側のメソッド名 ドット メソッド内メソッドの名前のように記述することで上書きすることができます。 この例では、、、、

ローカル変数の参照 外側のメソッドの全ローカル変数が メソッド内メソッドから参照、代入可能 メソッド内メソッドを上書きしていないクラス 外側のメソッドの全ローカル変数が    メソッド内メソッドから参照、代入可能 メソッド内メソッドを上書きしていないクラス グローバル変数のように見える 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変数は参照も代入も不可 カプセル化 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]; } 次に、メソッド内メソッドが上書きされた場合のローカル変数の参照について説明します。 本システムでは、メソッド内メソッドを含むようなメソッドを定義する際に、ローカル変数にpublic修飾子を付けて 宣言することができます。 このような変数をpublic変数と呼びます。 メソッド内メソッドを上書きする場合、 その上書きするメソッド内メソッド、サブクラスのメソッド内メソッドから 値の参照、変数への代入ができるのは、public変数のみになります。 例えば、この例では、 右側のサブクラスでメソッド内メソッドを上書きしていますが、 このサブクラスで定義されたメソッド内メソッドからアクセスできるのは 外側のメソッドのpublic 変数のみです。 public変数の値の参照、public 変数への代入は可能ですが public の付いていない変数aveの値をサブクラスで上書きするメソッド内メソッドcalcSumから 参照したり、aveに代入することはできません このような制限を設けることによって、サブクラスから、スーパークラスのローカル変数を自由に変更できないようになり、 カプセル化を守ることになると考えます。

メソッド内メソッドのスコープ メソッド内メソッドの有効範囲 ひとつ外側のメソッドのボディ 自身のメソッドボディ void f() { void g() { void h() { } .. void f() { void g() {} g(); }               void f() { void g() { g(); } メソッド内メソッドのスコープ・有効範囲について説明します。 メソッド内メソッドは、それを呼ぶことのできる範囲に制限があります。 呼ぶことのできるのは、一つ外側のメソッドボディと、 そのメソッド内メソッドのボディ内のみです。 この例は、メソッドfの中にメソッド内メソッドg、 gの中にメソッド内メソッドhが定義されている場合ですが、 この時、メソッド内メソッドgを呼ぶことができるのは、 gの一つ外側のメソッドfのボディと g自身のメソッドボディのみとなります。 gの中に定義されたhのボディや、 fでないメソッドからは呼ぶことはできません

他の方法: 参照渡し C++における参照渡し 変数への代入が可能 大量の引数 Javaには無い 呼ぶ側から値渡しと 区別がつかない 呼ぶ側から値渡しと 区別がつかない 副作用の有無 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) { 本研究では、メソッドの一部を切り出し、変更する方法として 上書き可能なメソッド内メソッドを提案しました。 切り出し、変更を行う方法として、 他に考えられる案があります。 一つはC++等における参照渡しです。 参照渡しを用いれば、異なるメソッドからその変数への代入が可能になります。 メソッドの一部を別のメソッドとして定義し、 代入が必要な変数は、参照渡しをすることで メソッドの一部を切り出すことができ、 それを上書きすることでメソッドの一部を変更することができます。 しかし、この方法を採った場合には、 必要な変数全てを引数として渡さなければならず、 場合によっては大量の引数が必要となります。 また、切り出されたメソッドを呼ぶ側からは値渡しと区別がつかず、 副作用の有無が分からないという問題があります

他の方法: クロージャ ローカル変数にアクセスできる 上書きするとアクセスできない 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種類用意 メソッド内メソッドをインライン展開したもの 展開しないもの メソッド内メソッドは通常のメソッドに変換 サブクラスでメソッド内メソッドを上書き サブクラスからはこちらが呼ばれる int calc(int[] a) { Var$calc $var = new Var$calc(); $var.sum = ..; : calcSum($var); } void calcSum(Var$calc $var) { 実装について説明します。 本システムは、コンパイラでコードを変換することによって実現します。 本システムでは、メソッド内メソッドの外側のメソッドを二種類用意します。 一つはメソッド内メソッドをインライン展開したもの。 もうひとつはメソッド内メソッドを展開しないものです。 メソッド内メソッドを上書きせずに外側のメソッドを呼ぶ場合には、 メソッド内メソッドを展開したものが呼ばれます。 サブクラスでメソッド内メソッドを上書きして外側のメソッドを呼ぶ場合には メソッド内メソッドを展開しないものが呼ばれます。 これは、メソッド内メソッドを展開したメソッドでは、メソッド内メソッドの上書きを反映させることができないからです。 メソッド内メソッドを通常のメソッドに変換 呼び出しを展開していない

効率的な実装 ソースコードを変換 メソッド内メソッドをインライン展開 再帰呼び出しが無く、上書きされないコードの場合 メソッド呼び出しのオーバーヘッドが消える 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]; } メソッド内メソッドを上書きせずに外側のメソッドを呼ぶ場合には、 メソッド内メソッドをインライン展開したメソッドが呼ばれます。 これによってメソッド呼び出し等のオーバーヘッドを消すことができます。 展開

外側のメソッドのコード変換 メソッド内メソッドをインライン展開しない メソッド内メソッドを通常のメソッドに変換 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; .. メソッド内メソッドをインライン展開しない メソッド内メソッドを通常のメソッドに変換 ローカル変数を集めたクラスを作成

メソッド内メソッドの上書きの実装 通常のメソッドに変換 スーパークラスにおいて通常のメソッドに変換されたメソッド内メソッドを上書き 変数へのアクセスはオブジェクトを介す 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 でメソッドを切り出さない 上書きの有無 100,000,000回実行 実験環境 OS: Windows 7 CPU: Intel Core i5         2.67GHz メモリ: 4GB 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

実験結果・コード量 本システムでは、差分のみの記述で変更が可能であるため、上書き時のコード量が少なくなる

関連研究 Regioncut [Akaiら’09] Closure Joinpoints [Bodden ’11] コード領域をジョインポイントとして選択 コード領域に対する変更が可能 ローカル変数への代入が不可能 Closure Joinpoints [Bodden ’11] コードブロックをジョインポイントとして選択 Beta [Knudenら’94] オブジェクト指向言語 上書き可能なインナープロシージャ スーパークラスの振る舞いが取り除けない

まとめと今後の課題 まとめ 今後の課題 メソッド内メソッド 効率的な実装 return の扱い 上書き ローカル変数を参照 インライン展開 メソッド内メソッドから? 外側のメソッドから?