EMFにおける拡張オブジェクトパターン

このブログエントリで、「拡張オブジェクト」パターンを学べる情報源とそのパターンの概要を簡単に紹介しました。実はEMFにおいても、拡張オブジェクトパターンが非常に重要な役割を担っています。

以下の簡単な図書館モデルを用いて、EMFにおいて拡張オブジェクトパターンがどのように利用されているかご紹介します。本パターンの利用方法を理解することにより、EMFをより有効に活用できると思います。

まず、上記モデルに対応する.ecoreファイルを作成し、それを基にModelコードを自動生成します。すると、各モデルクラスに対応するJavaBeanライクなJavaクラスだけでなく、以下のようなXXAdapterFactory.classというJavaクラスもXX.utilパッケージ内に生成されます。(以下のコードでは、コメント文を省略しています)

public class LibAdapterFactory extends AdapterFactoryImpl {

	protected static LibPackage modelPackage;

	public LibAdapterFactory() {
		if (modelPackage == null) {
			modelPackage = LibPackage.eINSTANCE;
		}
	}

	@Override
	public boolean isFactoryForType(Object object) {
		if (object == modelPackage) {
			return true;
		}
		if (object instanceof EObject) {
			return ((EObject)object).eClass().getEPackage() == modelPackage;
		}
		return false;
	}

	protected LibSwitch<Adapter> modelSwitch =
		new LibSwitch<Adapter>() {
			@Override
			public Adapter caseLibrary(Library object) {
				return createLibraryAdapter();
			}
			@Override
			public Adapter caseBook(Book object) {
				return createBookAdapter();
			}
			@Override
			public Adapter caseWriter(Writer object) {
				return createWriterAdapter();
			}
			@Override
			public Adapter defaultCase(EObject object) {
				return createEObjectAdapter();
			}
		};

	@Override
	public Adapter createAdapter(Notifier target) {
		return modelSwitch.doSwitch((EObject)target);
	}

	public Adapter createLibraryAdapter() {
		return null;
	}

	public Adapter createBookAdapter() {
		return null;
	}

	public Adapter createWriterAdapter() {
		return null;
	}

	public Adapter createEObjectAdapter() {
		return null;
	}

} //LibAdapterFactory

これは、まさにドメインモデルによるEclipseに対する依存性を取り除く、アダプタファクトリです。このアダプタファクトリは、図書館モデルに属する全てのクラスについてそれぞれアダプタを生成できるように、createBookAdapter()やcreateWriterAdapter()といったメソッドを備えています。このアダプタファクトリクラスの親であるAdapterFactoryImplクラスにadapt(Object target, Object type)というメソッドがあり、targetに応じてcreateBookAdapter()やcreateWriterAdapter()が呼ばれます。より正確には、adapt()メソッドにおいて、上記コード内のcreateAdapter()が呼ばれ、modelSwitchを用いて適切なcreateXXAdapter()が呼ばれます。

以上より、我々EMFユーザは、独自の機能(アダプタ)をドメインモデルに追加するには、上記アダプタファクトリを拡張することにより手軽に独自のアダプタファクトリを作成可能です。例えば、ドメインモデルの属性をプロパティビューに表示するために、IPropertySourceを実装するアダプタを追加することができます。あるドメインモデルオブジェクトtargetに対しIPropertySourceアダプタを追加したり取得したりするには、以下のように実行すればよいことになります。

AdapterFactory adapterFactory = new XXAdapterFactory();

IPropertySource source =
	(IPropertySource) adapterFactory.adapt(target, IPropertySource.class);

実際には、上記.ecoreファイルから、IPropertySourceを実装するアダプタを自動生成可能です。EMFが自動生成可能なコードには、Modelコード、Editコード、Editorコードの3種類(正確にはその他にTestコードも)があります。そのうち、Editコードというのがまさにそのようなアダプタの集合です。更に言うと、IPropertySourceだけでなく、ContentProvider、LabelProviderの機能も併せ持つItemProviderというアダプタをEditコードとして自動生成します。
Editorコードは、そのようなEditコードを利用したツリービューを主とするモデルインスタンスのエディタを提供します。

例えばEMFで自動生成したEditorコードの振る舞いや機能を変更/追加するには、自動生成されたコードを直接上書きしてもよいですが、上記のようなEMFにおける拡張オブジェクトパターンを理解していれば、Editコードに含まれるアイテムプロバイダを拡張したり、独自にアダプタを追加することができます。これにより、オブジェクト指向に基づいた、より拡張性/保守性の高いプロダクトを開発可能となりますね。