IDE を活用した言語機構に頼らないコード再利用のためのモジュール化 寺本裕基 武山文信 千葉滋 東京工業大学 情報理工学研究科 数理・計算科学専攻 IDEを活用した言語機構に頼らないコード再利用のためのモジュール化というタイトルで 東京工業大学の寺本裕基が発表させていただきます。 よろしくお願いします。
コピー&ペースト開発 同じようなコードを記述するとき使用 保守性の問題 一箇所変更すると他のモジュールも変更しなければならない class Rectangle extends Shape { List<Display> displays = new ArrayList<Display>(); int xpos, ypos; int width, height; void setX(int x) { xpos = x; for(Display d : displays) d.update(this); }} class Circle extends Shape { List<Display> displays = new ArrayList<Display>(); int centerX, centerY; int radius; void setCenterX(int x) { centerX = x; for(Display d : displays) d.update(this); }} まずはじめに、コードの再利用についてお話しします。 皆さんがコードの再利用をしようとした時、真っ先にしてしまうのはコピー&ペーストであると思います。 これは便利で使いやすいので、ついつい使ってしまいがちです。 しかしながら、コピー&ペーストを用いて開発を行うと、例えばバグが見つかった時などに複製したコードをすべてチェックして修正しなければなりません。 例えば、ここにあげた二つのコードは図形エディタプログラムの一部です。 図形エディタというのは、図形を描くことができるものです。 例えば、このパワーポイントのソースコードを囲っている四角形などの図形を描く処理では、こういうコードを書きます。 このようなプログラムの中で、図形の再描画処理というのは複数のモジュールの中に出現します。
関数抽象を用いたモジュール化 メソッドの利用 update() 処理を一つにまと めることで、保守性を上げる 変更は一箇所で済む class Shape { List<Display> displays = new ArrayList<Display>(); void updateDisplays() { for(Display d : displays) d.update(this); }} class Rectangle extends Shape { int xpos, ypos; int width, height; void setX(int x) { xpos = x; updateDisplays(); }} そこで、一般的にはメソッドなどの言語機構を用いてモジュール化を行うことによって保守性を上げます。 例えば、メソッドを利用してアップデート処理を親クラスに定義すると、バグが見つかっても修正するのは親クラスのほうだけで済みます。 先ほどの例のように、実は呼び出したかったメソッドがupdateではなく、redrawであったという場合には、親クラスの定義一箇所だけを変更すれば対応できます。 class Circle extends Shape { int centerX, centerY; int radius; void setCenterX(int x) { centerX = x; updateDisplays(); }}
言語機構を用いたモジュール化の限界 複雑な機構を導入すれば強力なモジュール化が可能 隠れたコスト パラメータ(関数引数) テンプレート クラス アスペクト ・・・ 隠れたコスト 新しい言語を導入しなければならない 複雑なので言語を理解するのが難しい 開発環境、ライブラリ、フレームワークなども用意しなければ ならない このように、言語機構を使ってモジュール化を行うと強力なモジュール化が可能となります。 しかしながら、言語機構には、テンプレートやアスペクトなど複雑なものがたくさんあります。 これら以外にも、これから新しいモジュール化の方法が登場してくる可能性が十分にあります。 新しいモジュール化の方法が提案されるたびに、新しい言語機構が導入しなければなりません。 導入されたとしても、それが複雑だと理解するのが難しく、プログラマの負担となり得ます。 また、新しい言語の導入に伴い、その言語用の開発環境・ライブラリ・フレームワークなども用意しなければなりません。 これらの導入には非常に手間がかかります。
提案:IDE を活用したモジュール化 Eclipse 上でコピー& ペーストによるモジュール 化 機能強化 Java言語は拡張しない 他の言語は学習不要 保守性の問題を解決 機能強化 複製したコード領域の変更 に対した同期 手続き抽象に相当 そこで我々は統合開発環境Eclipseを利用して言語機構と同等なモジュール化を実現する方法を提案します。 我々の提案では、皆さんにとって最も身近であるコピー&ペーストを利用してモジュール化を行います。 通常のコピー&ペーストでは、先ほど述べたような保守性の問題点があります。 なので、複製したコードを同期させ、同じ状態に保つことで、これを解消します。 例えば、右側の図では緑色の部分が同期されています。
提案:IDE を活用したモジュール化 Eclipse 上でコピー& ペーストによるモジュール 化 機能強化 Java言語は拡張しない 他の言語は導入不要 保守性の問題を解決 機能強化 複製したコード領域の変更 に対した同期 手続き抽象に相当 updateをredrawに変更 この状態で、上側のupdateメソッドの呼び出しを、redrawメソッドの呼び出しに変更したとします。
提案:IDE を活用したモジュール化 Eclipse 上でコピー& ペーストによるモジュール 化 機能強化 Java言語は拡張しない 他の言語は導入不要 保守性の問題を解決 機能強化 複製したコード領域の変更 に対した同期 手続き抽象に相当 updateをredrawに変更 この時、同期を行っている下のモジュールにも変更が自動的に伝わるようになります。 そのため、何か修正したいことがあっても、複製している場所のうちどこか一箇所だけを変更すればよいので、 通常のコピー&ペーストでいう、いろんなところを修正しなければならないという問題は解消されます。 変更が自動で伝わる
display.update(centerX-r, centerY-r, 2*r, 2*r); パラメータ抽象も可能 class Rectangle extends Shape { int xpos, ypos; int width, height; void setX(int x) { xpos = x; display.update(xpos,ypos, width, height); }} class Circle extends Shape { int centerX, centerY; int radius; void setCenterX(int x) { centerX = x; display.update(centerX-r, centerY-r, 2*r,2*r); width,height); display.update(centerX-r, centerY-r, 2*r, 2*r); 引数は使用場所によっ て別のものにしたい 同期領域のうち部分的 に同期しないことも可能 しかし、同期するだけでは十分ではありません。 実際のプログラミング言語では、関数の引数などパラメータが存在します。 これに対応するために、同期しているコードのうち一部分は同期させないような指定ができるようにします。 例えば、右側のコードで緑色の部分は同期されているけれども、赤色で書かれた引数部分は同期しない。といった指定ができます。 これにより、コードを使う場所によって異なる引数が指定できるようになります。
アスペクト抽象 あちこちで同じコードが実行されるとき、どこで実行され るかを抽象化(一箇所にまとめる) あちこちで個別にメソッド呼び出ししない コードが実行される場所が一目でわかる コードに名前をつけられる ここまでで、メソッドと同様なモジュール化が実現できています。 もうひとつの例として、アスペクト抽象について考えます。 アスペクト抽象は、あちこちで同じコードが実行されるときに、それがどこで実行されるのか、一つにまとめます。 実際のアスペクト指向プログラミング言語AspectJの記述を見てみると、実行するコードに名前が付けられる。 コードが実行されている場所が人目で分かるといった特徴があげられます。 aspect Update { after(Shape s) : execution (* Rectangle.set*(..)) || (* Circle.set*(..)) { s.display.update(s.xpos, s.ypos, s.width, s.height); }} class Rectangle extends Shape { int xpos, ypos; int width, height; void setX(int x) { xpos = x; }}
アウトライン機能によるアスペクト抽象 同期領域には名前をつけられる 同期領域の場所の一覧を表示 同期の問題点を解消 コードに名前をつけられる Update Rectangle setX(int) setY(int) ・・・ Circle setCenterX(int) setCenterY(int) 同期領域には名前をつけられる コードに名前をつけられる 同期領域の場所の一覧を表示 コードが実行される場所が一目 でわかる 同期の問題点を解消 どこが同期されているのかわかりにくい 意図せず別のコードが書き換わってしまう可能性 そこで、アスペクト抽象と同様な機能も実現するために、本システムでアウトライン機能を提供します。 この機能では、同期している領域に名前をつけ、その名前でコードの管理ができるようになります。 例えば、右側に上げたように、コードにアップデートという名前をつけ、それを利用した場所が一覧表示されます。 この機能によって、同期したときの問題点、どこが同期されているのかわかりにくい、一箇所コードの修正を行ったのに、 意図せず他のコードが書き換わってしまうといった問題点が解消できます
デモ
関連研究:アスペクト抽象 AspectJ AJDT 引数が異なる処理は二つ のアスペクトを定義する必 要がある 提案方式は一つで良い アスペクトが織り込まれる 場所をエディタ上に表示 アスペクト指向プログラミン グの知識が必要 aspect RectangleUpdate { after(Rectangle r) : execution (* *.set*(..)) && this(r) { r.display.update(r.xpos, r.ypos, r.width, r.height); }} aspect CircleUpdate { after(Circle s) : execution (* *.set*(..)) && this(c) { c.display.update(c.centerX-c.r, c.centerY-c.r, 2*c.r, 2*c.r); }} 関連研究です。 AspectJはアスペクト抽象を行うことができる言語です。 AspectJ では引数が異なるような処理は二つのアスペクトを定義する必要があります。 しかしながら、我々の提案では、ただ単にコピー&ペーストをして同期指定をするだけで構いません。 AJDTはAspectJでの開発を支援するためのツールで、アスペクトが織り込まれる場所をエディタ上に表示させます。 AspectJ、AJDT共にアスペクト指向プログラミングの知識が必要となります。
その他の関連研究 Fluid AOP[Honら ‘06] Linked Editing[Toomimら ‘04] 複数のモジュールにまたがるコードを一度に編集可能 アスペクト指向プログラミングの知識が必要 Linked Editing[Toomimら ‘04] リファクタリング用のツール 既に存在するコードクローンを同時に編集可能 提案方式は開発初期から利用可能 Fluid AOPは~ Linked Editing は既に存在している似ているコード、コードクローンを同時に編集するためのツールです。 しかし、これは既に書かれているものを編集するためであり、我々の提案では開発を行う初期段階、設計の頃から本システムを使うことを考えることができます。
まとめ IDEを活用したモジュール化の提案 今後の課題 再利用したいコードを複製し、同期 一部分のみの同期を可能にすることでパラメータ抽象に対応 アウトライン表示によりアスペクト抽象 今後の課題 実装できていない部分の実装 本システムの評価