目次


カスタムSWTコンポーネントでのMVCの実装

Model-View-ControllerによるEclipseアプリケーションの統合がいかに容易かを学ぶ

Comments

MVCとは何か

MVCアーキテクチャー(またはデザイン・パターン)は、モデル、ビュー、およびコントローラーの3つの部品で構成されるグラフィカル・ユーザー・インターフェース(GUI)デザイン・スタイルです。MVCは、データからプレゼンテーションを切り離すだけでなく、そのデータに対する操作からもプレゼンテーションを切り離します。

MVCアーキテクチャーの実装は、他の種類のアプリケーションとは違います。主な違いは、ビジネス・ロジックまたはビュー・レンダリング・ロジックの配置と実装方法にあります。典型的なWebアプリケーションでは、プログラマーはすべてのMVC部品をデザインして実装しなければなりませんが、Eclipseには、制御やレンダリングのほとんどを行うAPIが用意されています。このため、EclipseのMVC実装をWebアプリケーションやその他の種類のアプリケーションと厳密に比較することはできません。

Eclipse JFace

Eclipse JFaceは、コンテンツ・プロバイダーとラベル・プロバイダーを使用してMVCアーキテクチャーを実装します。JFace APIは、テーブルやツリーなど、標準の(トリビアルでない)ウィジェットをラップして、構造化されたコンテンツ・プロバイダーとラベル・プロバイダーを実装します。ウィジェット・タイプに基づくさまざまなコンテンツ・プロバイダーを実装することができます。リスト中心のビューアーが構造化ビューアーを実装し、構造化された形式で(リスト形式で)コンテンツがウィジェット・アイテムにマップされます。

基底クラスはViewerという名前であり、構造化ビューアーの拡張です。Viewerは、ウィジェット・コンテナの役目を果たします。コンテンツ・プロバイダーは、構造化された方法でデータをフェッチします。同様に、ラベル・プロバイダーは、対応するラベルを取得します。JFaceビューアー実装はこのデータを取得して、対応する関連付けを設定し、そのデータ・セットでユーザー・インターフェース(UI)を更新します。選択、フィルタリング、およびソートも行います。

JFaceの実装方法

EclipseのViewおよびViewerは、JFace制御機能のほとんどをこなします。Viewer、すなわちMVCのビュー部品は、ウィジェット・コンテナの役目も果たします。これは、プレゼンテーション・コンポーネントです。

Eclipse Viewは、Viewer、コンテンツ・プロバイダー、およびラベル・プロバイダーをインスタンス化し、モデルの役目を果たし、値オブジェクトを保持し、それらをViewer内でinputElementとして設定します。

Viewを作成するには、createPartControl()メソッドを使用してViewerをインスタンス化します。リスト1は、デフォルトのツリー・ビューアーをインスタンス化します。ツリー・オブジェクトをパラメーターとしてコンストラクターを使用することによって、ツリーをカスタマイズしてツリー・ビューアーをインスタンス化することもできます。

リスト1.ExampleViewのCreatePartControlメソッド
public class ExampleView extends ViewPart 
{ ... public void createPartControl(Composite parent) 
{ // define a grid layout 
GridLayout layout = new GridLayout(); 
layout.numColumns = 1; 
layout.marginHeight = 0; 
layout.marginWidth = 0; l
ayout.horizontalSpacing = 0; 
layout.verticalSpacing = 1; 
parent.setLayout(layout); 
// create widgets createActionBar(parent);
 createTree(parent); 
// add context menu and listeners
viewer.addDoubleClickListener(this); viewer.addSelectionChangedListener(openAction);
 // register viewer so actions respond to selection getSite().setSelectionProvider(viewer);
 hookContextMenu(); 
}
private void createTree(Composite parent) 
{ 
viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
viewer.setContentProvider(new ExampleViewContentProvider()); viewer.setLabelProvider
(new ExampleViewLabelProvider()); 
viewer.setSorter(new ViewerSorter()); 
viewer.setInput(ModelManager.getExampleModel()); 

viewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); 
} ... }

別のクラスで、ContentProviderを実装します。これは、ビューアー・タイプに適したインターフェースを使用して、ビューにデータを提供するオブジェクトです。たとえば、IStructuredContentProviderまたはITreeContentProviderビューアーを実装することができます。

ContentProviderコードで次のメソッドの1つを実装することによって、ContentProviderをViewerに関連付けます。

  • getElements(Object parent)
  • getChildren(Object element)

注:JFaceフレームワークは、これらのメソッドを呼び出します。

リスト2.カスタムContentProviderの作成
public class ExampleViewContentprovide implements ITreeContentProvide {

MVCアーキテクチャーは、通常、複数のビューと1つのデータ・ソースで作られます。現在、Eclipseプラットフォームでは、1つのモデルに1つのビューしか関連付けることができません。しかし、アダプター・ビューを使用して同じデータにアクセスできる複数のビューを作成することもできます。ContentProviderクラスにinputChanged()メソッドを含めるだけです。Viewerに新しい入力セットが与えられたときには、inputChanged()メソッドを使用してContentProviderに通知します。inputChanged()メソッドは入力引数としてViewerを受け入れるので、複数のビューで1つのContentProviderを使用することができます。

リスト3.さまざまなビューアーでのinputChangedメソッドの使用
/** * Register content provider with model. */
 public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
 {
 if (newInput != null)
 		 { 
this.viewer = viewer;
this.model = (ExampleDelegate)newInput; this.model.addModelListener(this); 
}
 }

Eclipse SWTでのMVCの使用

最も一般的なGUIアプリケーションでは、要求されたデータを表示するためのレイアウトを作成したり、データを追加または変更するためのフォーム(UIなど)を作成します。図1のサンプル・アプリケーションは、カスタムメイドのフォームにXMLストレージからのデータを読み取り/編集可能モードで表示する方法を示しています。また、MVCアーキテクチャーにおける各コンポーネントの役割も示しています。

図 1. サンプル・アプリケーション
サンプル・アプリケーション
サンプル・アプリケーション

図2は、アーキテクチャー全体の理解に役立つように、アプリケーションのクラス図を示しています。

図 2. サンプル・アプリケーションのクラス図
サンプル・アプリケーションのクラス図
サンプル・アプリケーションのクラス図

コントロールの作成

ExampleViewは、アプリケーション全体のコンテナの役目を果たします。これは、createPartControlメソッドでアプリケーションを初期化します。

リスト4.CreatePartControlメソッドによるレイアウトの初期化
public void createPartControl(Composite parent) {
ExampleEditLayout _layout = new 
    ExampleEditLayout(parent,SWT.NONE,FieldMode.Read,new ExampleViewContentProvider());
        }

フォームとレイアウトの作成

基底のlayoutクラスは、さまざまな形式のアプリケーションが使用するグローバル・メソッドと宣言を定義します。コールバック・メカニズムとして機能するコンテナ・イベントのいくつかが、ここで登録されます。

リスト5.layoutのCreateControl メソッド
public void createControls(int style) {
GridData    gridData;
Text                textFld, subjectFld;
Control            toLabel, ccLabel, bccLabel;
Control            fromDateTime;
Control            control;
Button durationText;
Button submit;

GridLayout layout = new GridLayout(2, false);
layout.marginWidth = 0;
layout.marginHeight = 4;

setLayout(layout);

//Label
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_CENTER);
gridData.horizontalIndent = 10;
LabelFactory.create(this, 
  Messages.getString("ExampleEditLayout.Title"), gridData); //$NON-NLS-1$
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_CENTER);
gridData.horizontalIndent = 40;
LabelFactory.create(this, "", gridData);

//Text
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_CENTER);
gridData.horizontalIndent = 10;
control = LabelFactory.create(this, 
  Messages.getString("ExampleEditLayout.Email"), gridData); //$NON-NLS-1$
gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING
| GridData.VERTICAL_ALIGN_CENTER);
gridData.horizontalIndent = 10;
control = TextFactory.create(this,
  SWT.BORDER| SWT.V_SCROLL | SWT.WRAP, gridData, FieldMode.Edit); //$NON-NLS-1$
addField(new TextField(control, ExampleViewContentProvider.FIRST_INDEX));

//Combo
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_CENTER);
gridData.horizontalIndent = 10;
LabelFactory.create(this, 
  Messages.getString("ExampleEditLayout.Group"), gridData); //$NON-NLS-1$
gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING
| GridData.VERTICAL_ALIGN_CENTER);
gridData.horizontalIndent = 40;
control = ComboFactory.create(this, 
  FieldMode.Edit, false, gridData); //$NON-NLS-1$
addField(new ComboField(control,
ExampleViewContentProvider.SECOND_INDEX));
...}

Field(ビュー)の作成

Fieldは、さまざまなUIコントロールとそれらのコントロールをグローバルに識別するために関連付けられたIDとを含めるためのメソッドを定義する抽象クラスです。各UIコントロールは、Fieldをサブクラス化して、コンテンツ・プロバイダーの読み取り/書き込み能力を与えます。リスト6は、LayoutクラスのFactoryパターンを使用してFieldを作成します。

リスト6.Fieldクラスを使用したテキスト・オブジェクトの作成
public class TextField extends Field {
    /**
		  * @param control
		  * @param id
		  */
    public TextField(Control control, int id) {
        super(control, id);
    }

    /* Based on the ID of the widget, values retrieved from 
     * the content provider are set.
     */
    public  void readFromContent(IExampleContentProvider content) {
        String newText = (String )content.getElement(getId());
        if (newText != null)
            ((Text )_control).setText(newText);
    }
    /* Based on the ID of the widget, values retrieved from widget are 
     * sent back to the content provider.
     */
    public void writeToContent(IExampleContentProvider content) {
        String newText = ((Text )_control).getText();
        content.setElement(getId(), newText);
    }
}

コンテンツ・プロバイダー(Model)の単純化

ExampleViewContentProviderは、IStructuredContentProviderを拡張するモデル・リスナーの役目を果たします。Eclipse APIの単純な実装であり、データ取得のためのコールバックを備えています。データを要求する各アイテムは、ビューの作成時にレイアウトで定義されたアイテムのユニークIDに基づきます。

メソッド・コールは、定義された各グローバルIDに関連付けられたデータを返します。リスト7に示されているコンテンツ・プロバイダーでは、XMLファイルから、またはアダプターを使用してデータベースからデータを取得することができます。

リスト7.カスタムContentProviderでのメソッドの実装
public Object getElement(int iIndex) {
        switch (iIndex) {
        case FIRST_INDEX: return "developer@ibm.com";
        case SECOND_INDEX : return new Integer(1);
        case FOURTH_INDEX : return new Boolean(true);
        case THIRD_INDEX: return new Boolean(false);
        case FIFTH_INDEX: return new Boolean(false);
        }
        return null;
    }

コントロールを作成して、レイアウトを初期化すると、Formがコンテンツ・プロバイダーに、コントロールIDを使用してFormコントロールにデータを入れるように要求します。

リスト8.レイアウトを初期化して、コントロールにデータを入れるフォーム
public Form (Composite parent, int style, FieldMode mode, ExampleViewContentProvider content) {
            super(parent, style);
            _content = content;
            _style = style;
            setMode(mode);
            init(style);
    }
	
    private void init(int style) {
            createControls(style);
        controlsCreated();
    }

protected void controlsCreated() {
            readFromContent();
    }

まとめ

Webアプリケーションは、MVCアーキテクチャー・スタイルの初期の実装でした。しかし、Eclipseのようなシンプルでパワフルなアプリケーション開発プラットフォームの登場により、プログラマーは、よりリッチなUIを短時間で、また、最小限の複雑さで容易に開発できるようになりました。


ダウンロード可能なリソース


関連トピック

  • Eclipse.orgのサイトを見てください。Eclipseとその使い方に関して、全体的な情報を得ることができます。
  • Eclipse Platform入門」(developerWorks, 2002年11月)を読んでください。Eclipseとプラグインのインストール方法の詳細を含めて、Eclipseの歴史と概要が解説されています。
  • developerWorksのJava technologyゾーンで、皆さんのJava技術を磨いてください。
  • developerWorksのWeb developmentゾーンには、皆さんのためにWeb開発ソリューション関連の資料が用意されています。
  • 無料のWebSphere Application Server Version 6.0試用版をダウンロードしてください。
  • 皆さんの次期開発プロジェクトを、IBM trial softwareを使って構築してください。developerWorksから直接ダウンロードすることができます。
  • The Linux Kernel Archivesから、CogitoとLinuxカーネル・ソースを入手してください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Java technology, Open source, XML
ArticleID=237805
ArticleTitle=カスタムSWTコンポーネントでのMVCの実装
publish-date=01112006