LoginSignup
1
1

More than 5 years have passed since last update.

Eclipse 4 (e4) Tutorial - 3.x からEclipse 4 (e4)への緩やかな移行

Posted at

Eclipse 4 (e4) Tutorial - Soft migration from 3.x to Eclipse 4 (e4)


(訳注: 前の回では次にDIの詳細について触れるとあったが、それは次の回に回されている?)

この連載の以前のパートでは、アプリケーションモデルを生成し、それを実装にリンクし、 アプリケーションモデルを拡張する方法、さらに依存性注入の詳細を説明しました。
このチュートリアルでは、Eclipse4 (e4)プログラミングモデルへの穏やかな移行方法を 説明します。このチュートリアルの基本的な目標は、開発において、依存性注入、アノテーションなどの新しい概念を 使えるようにすることです。
ただし、最初は完全なアプリケーションの移行は求めないこととします。そのため、3.xを要求するすべての既存のプラグインや枠組みは、いまだ以前と同じように使うことができます。
しかし、e4プログラミングモデルに沿ってアプリケーションの新たなUIコンポーネントを 開発することには、2つのアドバンテージがあります。

1. 新たなコンポーネントは、POJOであり、それゆえ自由度が高く、テストが容易で、再利用性が高いです
2. もし、アプリケーションがEclipse4アプリケーションプラットフォームに移行する場合、これらの コンポーネントは、既にe4で使用できる状態になります。

おもしろいことに一番目に挙げたアドバンテージは、たとえ、近々にEclipse4を使うことを考えていないとしても、活用する価値があります。この考えは、実際にたいへんシンプルで特に目新しいものではありません。

3.xでのPOJO

基本的な概念は、カスタムアプリケーションに対するコードと、そのコンポーネントを Eclipseワークベンチに組み込むためのコードを明確に分離させることです。
2つ目のグループのコードは、ワークベンチAPIに依存しており、それゆえ、Eclipseのバージョンすなわち、3.xや4.xに特化したものになります。1つ目のグループのコードは、Eclispeのバージョンに依存させる必要はありません。 事実、ワークベンチのことは少しも知る必要がないのです。
wrapper 300x159 Eclipse 4 (e4) Tutorial Soft migration from 3.x to Eclipse 4 (e4)
このような分離の良い例として、ハンドラの実装を挙げることができます。ハンドラを実装するために、Eclipse 3.xでは、ハンドラはコマンドに結び付けられ、 IHandlerインタフェースを実装する必要があります。
3.xでの典型的なハンドラの例を見てみましょう。それは、現在の選択に基いて何かを行います。この例では、ハンドラは現在の選択がMailAccount型かどうかチェックします。もし、その結果が真なら、ユーザがログイン済みかチェックし、メールを送受信します。

public Object execute(ExecutionEvent event) throws ExecutionException {
 ISelection currentSelection = HandlerUtil.getCurrentSelection(event);
 if (currentSelection instanceof IStructuredSelection) {
  Object firstElement = ((IStructuredSelection) currentSelection)
  .getFirstElement();
 if (firstElement instanceof MailAccount) {
  MailAccount account = (MailAccount) firstElement;
 if(!account.isLoggedIn()){
  account.logIn();
 }
 account.sendMails();
 account.recieveMails();
 }
}
 
return null;
}

この設計には3つの主要な問題があります。定型的なコード記述、テスト容易性の不足、再利用性の不足 です。
このハンドラに対して、テストケースを書いた場合を想像してみましょう。あなたは、手でExecutionEventを生成し、さらにHandlerUtilがテスト環境で利用可能かどうかを確かめる必要があります。この場合の選択は、単純なフィールドでなく、プロパティであることから、 モックのExecutionEventを適切に準備する方法を理解するために、 HandlerUtil.getCurrentSelection()の実装を見なくてはならないでしょう。
最後のセクションでは、さらに定型的なテストコードをテストコード内に持つことになります。もし、テストケースの作成をあなたが管理することになったとした場合、 タイマーに基いたメールの同期を引き起こしたい、要するに、 その実行メソッドを直接呼び出したいと考えることを想像してみましょう。 そのハンドラを再利用するためには、ExecutionEventを生成する必要があるはずです。もし、ハンドラがあなたの制御下にあるなら、たぶん、この時点でリファクタリングするでしょう。
しかし、ハンドラはリファクタリング不可能なフレームワークの中にあるかもしれません。
これに対する解決策は、とてもシンプルで、コードを2つのメソッドに分離することです。最初のメソッドは、ワークベンチに特化した部分、すなわち、選択をアンパックするところ を扱います。2番目のメソッドは、ビジネスロジックを実行するためのもので、このケースではスタティックメソッドにできます。

public Object execute(ExecutionEvent event) throws ExecutionException {
 ISelection currentSelection = HandlerUtil.getCurrentSelection(event);
 if (currentSelection instanceof IStructuredSelection) {
  Object firstElement = ((IStructuredSelection) currentSelection)
  .getFirstElement();
  if (firstElement instanceof MailAccount) {
   synchronizeAccount((MailAccount) firstElement);
  }
 }
 
return null;
}
 
public static void synchronizeAccount(MailAccount account) {
 if(!account.isLoggedIn()){
  account.logIn();
 }
 account.sendMails();
 account.recieveMails();
}

この設計で、2番目のメソッドに対するテストケースは書きやすくなります。加えて、そのメソッドは、他のどの場所からも簡単に呼び出されることができます。 例えばタイマーベースの同期をトリガすることが挙げられます。最後に、コードが理解しやすくなります。
次のステップですが、2番目のメソッドをハンドラから外に、例えばワークベンチ依存がない プラグインに移動することができます。 これと同じデザインパターンをビューに適用し、同様の利点を得ることができます。
我々は一つのワークベンチに特化したクラスと、一つのPOJOになれるクラスを持っていることになります。
次の例では、WorkbenchViewは、現在の選択を扱うことも含めて、すべてのワークベンチ 特化部分を担います。その一方でPOJOViewは完全に独立しています。

public class WorkbenchView extends ViewPart {
 
private POJOView pojoView;
 
public WorkbenchView() {
 pojoView = new POJOView();
}
 
@Override
public void createPartControl(Composite parent) {
 pojoView.createPartControl(parent);
 ISelectionService service = (ISelectionService) getSite().getService(
 ISelectionService.class);
 service.addSelectionListener(new ISelectionListener() {
 
@Override
public void selectionChanged(IWorkbenchPart part,
 ISelection selection) {
 if (selection instanceof IStructuredSelection) {
  Object firstElement = ((IStructuredSelection) selection)
  .getFirstElement();
  pojoView.setInput(firstElement);
 
  }
}
});
 
}
 
@Override
public void setFocus() {
 pojoView.setFocus();
}
 
}
public class POJOView {
 
private Text text;
 
public void createPartControl(Composite parent) {
 text = new Text(parent, SWT.NONE);
}
 
public void setFocus() {
 text.setFocus();
 
}
 
public void setInput(Object object) {
 if(object!=null){
  text.setText(object.toString());
 }
}
 
}

繰り返しになりますが、今やPOJOViewは、とても理解しやすく、テストしやすく、そして 再利用しやすい状態です。例のようにPOJOViewは、JFaceウィザードに組み込もうと思えば可能です。ここまでで、Eclipse4特有の概念は何も使っていません。これまで出てきた設計パターンは、 ただの3.xで使うことができます。
(3.xのインタフェースを実装するための)ラッパークラスは、たいていこれと良く似ているので、 いくつかの一般的な実装を準備することは容易でしょう。次のセクションでは、そのような依存性注入を使う一般的な実装を紹介します。

3.xにおける依存性注入

ワークベンチ依存のコードと、POJOのようなカスタムコンポーネントを分離すると、 3.xでさえ、コンポーネントの再利用とテストが容易になります。しかし、Eclipse4アプリケーションプラットフォーム向けのコンポーネント開発と比較すると、 2つの不利な点があります。

1. ラッパーは手で実装し、拡張ポイントを等して登録しなければなりません
2. コンポーネントの実装に依存性注入を使うことができないので、Eclipse4でそのまま使えません。

ひとたび、あなたのアプリケーションがEclipse 4.x版で動けば、まだ3.xのAPIを使っているとしても この問題への解決策はあります。 その解決策とは、トムシンディによって開発が進められているe4ツールプロジェクトから 提供される 3.x e4ブリッジです。このプラグインは、基本的にジェネリックばラッパークラスを提供し、 それは、3.xアプリケーションで使うことができます。
そのラッパークラスは、POJOである第2のクラスを定義することを可能とし、対応する コンポーネントを実装します。この解決策は、以前述べたのと同様のパターンに従いますが、一般的な方法で動作し、 依存性注入をサポートします。執筆時点では、ビューとエディターの実装が利用できます。3.x のワークベンチラッパーを生成するためには、それぞれの型、例えば、 Viewを実装するDIViewPartなどから単純に派生させます。
このラッパークラスはほとんど空です。やらなければならないことは、コンポーネントを実装するPOJOを指定することです。

public class ExampleViewWrapper extends DIViewPart{
public ExampleViewWrapper() {
 super(ExampleView.class);
}
}

このクラスは、3.xでやっていたようにViewの拡張ポイントを使って登録されます。

ビュー自身の実装は、POJOにすることができ、それゆえ依存性注入が利用できます。今々のプログラミングの利便性に加えて、コンポーネントがe4に移行される場合に 何らかの編集なしに使うことができます。
このケースでは、ラッパーと拡張記述を取り除き、POJOViewをアプリケーションモデルに統合する ことができます。見ればわかるように、ビューは、現在の選択への注入も含む依存性注入のすべての 機能を使うことができます。

public class ExampleView {
private Label label;
@Inject
public ExampleView(Composite parent){
 label = new Label(parent, SWT.NONE);
 label.setText("Hello World");
}
 
@Inject
public void setInput(@Optional @Named(IServiceConstants.ACTIVE_SELECTION)Object input){
 if(input==null){
  return;
 }
 label.setText(input.toString());
}
}

これがどうやって動作しているかを理解するために、もっともシンプルなケースである、 ハンドラ―に対するラッパー(私が最近コントリビュートしました)を見てみましょう。
例を単純にするために、今は@CanEnableアノテーションを無視しましょう。DIHandlerは、3.xでやっていたように、ハンドラ―拡張ポイントで登録できるように、 3.x IHandlerインタフェースを実装する必要があります。加えて、DIHandlerは、ラップしているPOJOクラスについて知る必要があります。このPOJOクラスは、ラッパーによって初期化されます。これを行うには、e4 ContextInjectionFactoryを使用します。アプリケーションは、互換性レイヤで動作しているので、EclipseContextをサービスとして 読み出すことができ、クラスを生成するために使うことができます。
このようにハンドラによって期待されるすべてのフィールドは、注入されます。 (e4で標準的にやっているように)

public class DIHandler extends AbstractHandler {
 
private Classclazz;
private C component;
 
public DIHandler(Classclazz) {
 this.clazz = clazz;
 IEclipseContext context = getActiveContext();
 component = ContextInjectionFactory.make(clazz, context);
}
private static IEclipseContext getActiveContext() {
 IEclipseContext parentContext = (IEclipseContext) PlatformUI.getWorkbench().getService(
 IEclipseContext.class);
 return parentContext.getActiveLeaf();
}

唯一今欠けているものは、実行メソッドの実装です。再度、@Executeを付けたPOJOのメソッドを呼び出すために、単純にInjectionFactoryを使用します。

public Object execute(ExecutionEvent event) throws ExecutionException {
 return ContextInjectionFactory.invoke(component, Execute.class,
 getActiveContext());
}

このDIHandlerは、それほど複雑なものではなく、POJOなハンドラーを3.xワークベンチにラップする ことができます。

まとめ

このチュートリアルは、3.x から Eclipse4プログラミングモデルへのソフトな移行に対する アプローチを述べました。
カスタムUIコンポーネントとワークベンチ依存クラスの実装を分離する概念の説明から始めました。これは、コンポーネントの再利用性とテスト容易性を改善します。ブリッジプラグインを使うと、e4ツールラッパークラスを手動で実装する必要が無くなります。加えて、DIWrapperを使うと、3.xでのプログラミング時にも依存性注入が使えるようになります。
このような方法で開発されたコンポーネントは、何らかの適合処理を加えることなく、純粋な e4アプリケーションに統合することが可能となります。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1