細かい粒度でコードの再利用を可能とするメソッド内メソッドのJava言語への導入

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 継承されたくないことを明示する。これ以上機能拡張 / 変更でき.
6.4継承とメソッド 6.5継承とコンストラクタ 11月28日 時田 陽一
Javaのための暗黙的に型定義される構造体
第5回 iPhoneアプリ開発勉強会 Objective-C 「継承とクラス」
アルゴリズムとデータ構造1 2007年6月12日
情報伝播によるオブジェクト指向プログラム理解支援の提案
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
アルゴリズムとデータ構造 2011年6月13日
アルゴリズムとプログラミング (Algorithms and Programming)
同期的にアドバイスを活性化できる分散動的アスペクト指向システム
第6回独習Javaゼミ 第6章 セクション4~6 発表者 直江 宗紀.
ソースコードに対する適用可能な修正手順を 可視化するリファクタリング支援手法の提案
ソフトウェア工学 知能情報学部 新田直也.
プログラミング言語入門 手続き型言語としてのJava
アルゴリズムとプログラミング (Algorithms and Programming)
細かい粒度で コードの再利用を可能とする メソッド内メソッドと その効率の良い実装方法の提案
第9章 例外処理,パッケージ 9.1 例外処理 9.2 ガーベッジコレクション.
独習JAVA 6.8 コンストラクタの修飾子 6.9 メソッドの修飾子 6.10 ObjectクラスとClassクラス 11月28日(金)
オブジェクト指向 プログラミング 第十一回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第十三回 知能情報学部 新田直也.
暗黙的に型付けされる構造体の Java言語への導入
ポインタ解析におけるライブラリの スタブコードへの置換の効果
オブジェクト指向プログラムにおける エイリアス解析手法の提案と実現
第11週:super/subクラス、継承性、メソッド再定義
ユーザ毎にカスタマイズ可能な Webアプリケーションの 効率の良い実装方法
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
動的データ依存関係解析を用いた Javaプログラムスライス手法
C#言語ソースプログラムの原型 C言語 C#言語 Hello World! Hello Students! オマジナイ! 適当なクラス名
オブジェクト指向言語論 第十一回 知能情報学部 新田直也.
アルゴリズムとデータ構造1 2005年7月5日
もっと詳しくArrayクラスについて調べるには → キーワード検索
アルゴリズムとデータ構造1 2005年6月24日
アルゴリズムとデータ構造 2010年6月21日
アスペクト指向言語のための 独立性の高いパッケージシステム
バイトコードを単位とするJavaスライスシステムの試作
オブジェクト指向 プログラミング 第十ニ回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
pointcut に関して高い記述力を持つ アスペクト指向言語 Josh
オブジェクト指向プログラミングと開発環境
オブジェクト指向言語論 第十一回 知能情報学部 新田直也.
プログラムの織り込み関係を可視化するアウトラインビューの提案と実装
アルゴリズムとプログラミング (Algorithms and Programming)
プログラミング言語論 第十三回 理工学部 情報システム工学科 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
プログラミング言語論 第十一回 理工学部 情報システム工学科 新田直也.
統合開発環境によって表現された 言語機構によるコードのモジュール化
同期処理のモジュール化を 可能にする アスペクト指向言語
C#プログラミング実習 第3回.
計算機プログラミングI 木曜日 1時限・5時限 担当: 増原英彦 第1回 2002年10月10日(木)
アルゴリズムとデータ構造 2012年6月11日
「マイグレーションを支援する分散集合オブジェクト」
プログラムの差分記述を 容易に行うための レイヤー機構付きIDEの提案
アルゴリズムとデータ構造1 2009年6月15日
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
状況に応じて適切な 例外処理が行なえる アスペクト指向分散環境実験の 支援ツール
メンバとメソッド C言語の構造体 変数の集まり C#言語のクラス + それを処理する関数の集まり フィールド または メンバ変数 メンバ
統合開発環境のための プログラミング言語拡張 フレームワーク
オブジェクト指向言語論 第九回 知能情報学部 新田直也.
アルゴリズムとデータ構造 2010年6月17日
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
回帰テストにおける実行系列の差分の効率的な検出手法
Javaとは Javaとはオブジェクト指向言語でJava VM(Java仮想マシン)と呼ばれるプログラム上で動作します。
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
オブジェクト指向言語における セキュリティ解析アルゴリズムの提案と実現
オブジェクト指向言語論 第九回 知能情報学部 新田直也.
オブジェクト指向言語論 第十回 知能情報学部 新田直也.
計算機プログラミングI 第10回 2002年12月19日(木) メソッドの再定義と動的結合 クイズ メソッドの再定義 (オーバーライド)
Presentation transcript:

細かい粒度でコードの再利用を可能とするメソッド内メソッドのJava言語への導入 理学部 情報科学科 07-22331 平松 俊樹 指導教員 千葉 滋 教授

巨大メソッドの一部の再利用 一部だけ上書きしたい メソッドに切り出す ローカル変数を参照して いたら? 変更 class Parser { private Object parse(TokenStr in) { while (..) { Symbol token = .. ; short act = .. ; if (..) { : } else { report.syntaxError(token); recoverFromError(token, in); 一部だけ上書きしたい メソッドに切り出す ローカル変数を参照して いたら? 変更 プログラミングの際に、巨大なメソッドの一部だけを上書きして変更し、 残りの部分を再利用したい場合があります。 例えば巨大なループの一部を変更したい場合などです。 このようなときに、その変更したい部分を別のメソッドとして切り出し、 その切り出されたメソッドを、サブクラスにおいてオーバーライドするという方法が考えられます。 しかしこの方法では、その変更したい部分がローカル変数を参照していたときに問題が起きます。 この例では・・・ 1’

メソッドに切り出すことは困難 大量の引数 変数への代入は? 切り出されたメソッド class Parser { private Object parse(TokenStr in) { while (..) { Symbol token = .. ; short act = .. ; if (..) {..} else {elseM(token, in);} } void elseM(Symbol token,TokenStr in){ report.syntaxError(token); recoverFromError(token, in); 大量の引数 変数への代入は? 変更したい部分を別のメソッドとして切り分けた場合には、 その切り分けられた部分から元のメソッドのローカル変数にアクセスすることは不可能です。 このため、メソッドに切り分ける際には、必要なローカル変数を全て引数として渡す等の 作業が必要になり、場合によっては大量の引数を扱わなければならなくなります。 引数に渡すことで、切り分けられたメソッドで 元のメソッドのローカル変数を使うことが可能となりますが、 切り分けられたメソッドから元のメソッドのローカル変数への代入を行うことができません。 2’ 切り出されたメソッド

クロージャを用いた場合 ローカル変数にアクセスできる アクセスできない 上書きするとアクセスできない class Parser{ Closure elseM; private Object parse(TokenStr in) { while (..) { Symbol token = .. ; short act = .. ; if (..) {..} else { elseM = {report.syntaxError(token); recoverFromError(token, in); } elseM(); }}}}} class SubParser extends Parser{ private Object parse(TokenStr in) { elseM = { report.syntaxError(token); act = 0; } super.parse(in); アクセスできない 上書き 先ほどの、ローカル変数にアクセスできないという問題は、クロージャを用いることによって解決できます。 これを例を使って説明します。 なお、現在のJavaにはクロージャが無いため、クロージャの記述は仮のものです。 左のコードがスーパークラスです。 フィールドとしてクロージャを保持し、これをメソッド内で呼び出しています。 このとき、定義されたクロージャはメソッドのローカル変数にアクセスすることができます。 これに対して、右のコードがサブクラスであり、 メソッド内で別のクロージャを定義し、フィールドへ代入してクロージャを上書きしたうえで、 元のメソッドを呼び出しています。 このようにすると、メソッドの一部を変更することができますが、 サブクラスで定義したクロージャからは元のメソッドのローカル変数にアクセスすることができず、 この例では右のコードのクロージャからスーパークラスのメソッド内の token, actを参照できず、実行が行えません。 3’

提案:上書き可能なメソッド内メソッド サブクラスでオーバーライド可能 class Parser { private Object parse(TokenStr in) { while (..) { Symbol token = .. ; short act = .. ; if (..) {..} else { void elseM() { report.syntaxError(token); recoverFromError(token, in); } elseM(); }}}} class Session { public void buy(Item item) { int count = 0; public int numItem = 0; public int totalAmount = 0; boolean inService = false; : void service() { if (inService) { numItem++; count++; } service(); class SubParser extends Parser{ parse(TokenStr).elseM() { report.syntaxError(token); act = 0; } class Discount extends Session{ int limit; public void buy(Item).void service(){ if (numItem > limit) { totalAmount *= 0.8; } elseMのみを上書き これらの問題を解決するために本研究が提案するのは 上書き可能なメソッド内メソッドです。 本システムの概要を述べさせていただきます。 まず、メソッドの内部にメソッドを定義することが可能です。 左のコードがメソッド内にメソッドを定義した例です。 赤字の部分がメソッド内メソッドの定義です。 この例では通常のメソッドparseの内部にメソッド内メソッドelseMを定義しています。 このメソッド内メソッドからは、 自身が定義されたメソッドのローカル変数と引数が参照可能です。 変数の値を得るだけでなく、変数への代入も可能です。 これによって、メソッドの一部を別のメソッドとして切り分けた際の、 引数の受け渡しや、ローカル変数に代入ができないという問題を解決できます。 この例では、メソッド内メソッドelseMの内部から、 メソッドparseのローカル変数token, 引数inを参照しています。 メソッドの定義だけでは実行はされないため、改めてメソッド内メソッドを呼び出す必要があります。 また、メソッド内メソッドはサブクラスでオーバーライドすることができます。 この例では右のサブクラスで、メソッド内メソッドelseMをオーバーライドしています。 オーバーライドするメソッド内メソッドからも、もとのメソッドのローカル変数を参照できます。 これによって、クロージャを用いた際の、 クロージャを上書きするとローカル変数にアクセスできなくなるという問題を解決できます。 4’30’’

ローカル変数へのアクセス public宣言されたローカル変数、引数 サブクラスのメソッド内メソッドから参照可能 カプセル化を破壊しない class Parser { Object parse(public TokenStr in) { public Symbol token; short act; void elseM() { : } parse in token act アクセス可 メソッド内メソッドを定義すると、そのクラスにおいては、メソッド内メソッドからは、 元のメソッドの全てのローカル変数にアクセス可能です。 しかし、サブクラスでオーバーライドしたメソッド内メソッドからも 全てのローカル変数にアクセスできてしまうと、 これはカプセル化に反すことになります。 このため、本研究ではメソッド内メソッドをオーバーライドした場合には、 サブクラスからアクセスできるローカル変数、引数は public宣言されたもののみという制限を設けています。 この例では・・・ 5’30’’ class SubParser extends Parser { Object parse(TokenStr).elseM() { : } elseM アクセス不可

実装方法 JastAddを用いて実装 メソッド内メソッドで参照される変数を集めた オブジェクトを作る 今回採用した方法 それを引数で渡す クロージャの実装方法と類似 今回採用した方法 メソッドに対応するクラスを作成 コード変換が簡単 thisの扱いが複雑 コード量 約8000行を読み、1100行を記述 本システムの実装方法について説明します。 本システムはJastAddを用いて実装を行いました。 一般的には、メソッド内メソッドで参照される変数を集めたオブジェクトを作り、 それを引数で渡すという クロージャの実装方法と類似した手法をとりますが、 コード変換を簡便にするため、今回は メソッドに対応するクラスを作成し、 メソッドのローカル変数を対応するクラスのフィールドに変換するという方法をとりました。 この方法を用いると、新たにクラスが作成されるため、 メソッド内のThisが指すものを正しいオブジェクトに戻すための変換が必要になります。 6’30’’

コード変更の例 class C { int outerM(int arg) { return new C$outerM(this).run$$(arg); } void innerM(C$outerM $outer) { $outer.new C$innerM($outer).run$$(); class C$outerM { public C $this; private int arg; protected int localVar; : public int run$$(int arg$arg) { arg = arg$arg; localVar = 0; $this.innerM(this); class C$innerM { public C$outerM $outer; public void run$$() { localVar = arg; }}}} コード変更の例 class C { int outerM(int arg) { public int localVar = 0; void innerM() { localVar = arg; } InnerM(); : オーバーライド class Child extends C { void innerM(C$outerM $outer) { $outer.new C$innerM($outer) { public void run$$() { $outer.localVar = 0; } }.run$$(); class Child extends C { int outerM(int).void innerM(){ localVar = 0; } 7’30’’

実験:マイクロベンチマーク 実行時間の比較 代入するローカル変数の個数を変えて実験 メソッド内メソッド 手動で切り分けたメソッド 実験環境 OS: Windows 7 CPU: Intel Core i5 2.67GHz メモリ: 4.00GB JVM 1.6.0_20

実験1: 代入するローカル変数の個数=1 結果 メソッド内メソッドを呼ぶたび にオブジェクト作成 手動の場合はオブジェクトを 作らない public void method1() { int result = 0; public int method2() { int r1 = i + j; int r2 = i - j; int r3 = i * j; int r4 = i / j; return r1 + r2 + r3 + r4; } for (int i = 1; i <= 10000; i++) { for (int j = 1; j <= 10000; j++) { result = method2(); 結果 メソッド内メソッドを呼ぶたび にオブジェクト作成 手動の場合はオブジェクトを 作らない プログラム 実行時間(ms) 比 メソッド内 メソッド 1694 4.8 手動で分けたメソッド 352 1 プログラム 実行時間(ms) メソッド内メソッド 1694 手動で分けたメソッド 352 1694 352 8’

実験2: 代入するローカル変数の個数=4 結果 手動で書いたコードの変 数の渡し方が悪い プログラム 実行時間(ms) 比 メソッド内 public void method1() { int localVar1 = 0; int localVar2 = 0; int localVar3 = 0; int localVar4 = 0; public void method2() { localVar1 = i + j; localVar2 = i - j; localVar3 = i * j; localVar4 = i / j; } for (int i = 1; i <= 10000; i++) { for (int j = 1; j <= 10000; j++) { method2(); }}} 結果 手動で書いたコードの変 数の渡し方が悪い 効率よく書くのが難しい オーバーヘッドは許容範囲 プログラム 実行時間(ms) 比 メソッド内 メソッド 1261 1 手動で分けたメソッド 1515 1.2 プログラム 実行時間(ms) メソッド内メソッド 1261 手動で分けたメソッド 1515 public ReturnValue method2(int i, int j) { int r1 = i + j; int r2 = i – j; int r3 = i * j; int r4 = i / j; return new ReturnValue(r1, r2, r3, r4); } 手で切り分けたコードが必ず速いわけではない ローカル変数が複数ある場合を想定しているので、 先ほどのオーバーヘッドは許容範囲 8’30’’ 手動で書いたmehtod2

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

まとめ メソッド内メソッド 今後の課題 JastAddJを拡張してコンパイラを実装 オーバーライド可能 ローカル変数を参照可能 実装の改善 オーバーライドの記述の簡素化